Updates to GADC to use new simpler gfx queued bufferring.
NOTE: code is still buggy (or the one and only driver is buggy).
This commit is contained in:
parent
712ff73f77
commit
271f0c743f
@ -19,13 +19,18 @@
|
||||
|
||||
#define GAUDIO_RECORD_NUM_CHANNELS 1
|
||||
|
||||
/**
|
||||
* @brief Whether each channel is mono or stereo
|
||||
*/
|
||||
#define GAUDIO_RECORD_CHANNEL0_IS_STEREO FALSE
|
||||
|
||||
/**
|
||||
* The list of audio channels and their uses
|
||||
*/
|
||||
#define GAUDIO_RECORD_MICROPHONE 0
|
||||
|
||||
#ifdef GAUDIO_RECORD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
static uint32_t gaudio_gadc_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
GADC_PHYSDEV_MICROPHONE,
|
||||
};
|
||||
#endif
|
||||
|
@ -51,7 +51,6 @@
|
||||
#define GDISP_NEED_VALIDATION TRUE
|
||||
#define GDISP_NEED_CLIP TRUE
|
||||
#define GDISP_NEED_TEXT TRUE
|
||||
#define GDISP_NEED_CONTROL TRUE
|
||||
#define GDISP_NEED_MULTITHREAD TRUE
|
||||
|
||||
/* GDISP - builtin fonts */
|
||||
|
@ -38,9 +38,6 @@
|
||||
/* Include internal GWIN routines so we can build our own superset class */
|
||||
#include "src/gwin/class_gwin.h"
|
||||
|
||||
/* The size of our dynamically allocated audio buffer */
|
||||
#define AUDIOBUFSZ 64*2
|
||||
|
||||
/* How many flat-line sample before we trigger */
|
||||
#define FLATLINE_SAMPLES 8
|
||||
|
||||
@ -50,10 +47,6 @@ static void _destroy(GHandle gh) {
|
||||
gfxFree(((GScopeObject *)gh)->lastscopetrace);
|
||||
((GScopeObject *)gh)->lastscopetrace = 0;
|
||||
}
|
||||
if (((GScopeObject *)gh)->audiobuf) {
|
||||
gfxFree(((GScopeObject *)gh)->audiobuf);
|
||||
((GScopeObject *)gh)->audiobuf = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const gwinVMT scopeVMT = {
|
||||
@ -68,12 +61,9 @@ GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint
|
||||
/* Initialise the base class GWIN */
|
||||
if (!(gs = (GScopeObject *)_gwindowCreate(g, &gs->g, pInit, &scopeVMT, 0)))
|
||||
return 0;
|
||||
gfxSemInit(&gs->bsem, 0, 1);
|
||||
gs->nextx = 0;
|
||||
if (!(gs->lastscopetrace = gfxAlloc(gs->g.width * sizeof(coord_t))))
|
||||
return 0;
|
||||
if (!(gs->audiobuf = gfxAlloc(AUDIOBUFSZ * sizeof(adcsample_t))))
|
||||
return 0;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
gs->lasty = gs->g.height/2;
|
||||
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
@ -82,8 +72,7 @@ GHandle gwinGScopeCreate(GDisplay *g, GScopeObject *gs, GWindowInit *pInit, uint
|
||||
#endif
|
||||
|
||||
/* Start the GADC high speed converter */
|
||||
gadcHighSpeedInit(physdev, frequency, gs->audiobuf, AUDIOBUFSZ, AUDIOBUFSZ/2);
|
||||
gadcHighSpeedSetBSem(&gs->bsem, &gs->myEvent);
|
||||
gadcHighSpeedInit(physdev, frequency);
|
||||
gadcHighSpeedStart();
|
||||
|
||||
gwinSetVisible((GHandle)gs, pInit->show);
|
||||
@ -97,6 +86,8 @@ void gwinScopeWaitForTrace(GHandle gh) {
|
||||
coord_t yoffset;
|
||||
adcsample_t *pa;
|
||||
coord_t *pc;
|
||||
GDataBuffer *pd;
|
||||
uint8_t shr;
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
bool_t rdytrigger;
|
||||
int flsamples;
|
||||
@ -109,20 +100,21 @@ void gwinScopeWaitForTrace(GHandle gh) {
|
||||
if (gh->vmt != &scopeVMT)
|
||||
return;
|
||||
|
||||
/* Wait for a set of audio conversions */
|
||||
gfxSemWait(&gs->bsem, TIME_INFINITE);
|
||||
/* Wait for a set of conversions */
|
||||
pd = gadcHighSpeedGetData(TIME_INFINITE);
|
||||
|
||||
/* Ensure we are drawing in the right area */
|
||||
#if GDISP_NEED_CLIP
|
||||
gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height);
|
||||
#endif
|
||||
|
||||
shr = 16 - gfxSampleFormatBits(GADC_SAMPLE_FORMAT);
|
||||
yoffset = gh->height/2;
|
||||
if (!(GADC_SAMPLE_FORMAT & 1))
|
||||
if (!gfxSampleFormatIsSigned(GADC_SAMPLE_FORMAT))
|
||||
yoffset += (1<<SCOPE_Y_BITS)/2;
|
||||
x = gs->nextx;
|
||||
pc = gs->lastscopetrace+x;
|
||||
pa = gs->myEvent.buffer;
|
||||
pa = (adcsample_t *)(pd+1);
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
rdytrigger = FALSE;
|
||||
flsamples = 0;
|
||||
@ -132,14 +124,10 @@ void gwinScopeWaitForTrace(GHandle gh) {
|
||||
scopemin = 0;
|
||||
#endif
|
||||
|
||||
for(i = gs->myEvent.count; i; i--) {
|
||||
for(i = pd->len/sizeof(adcsample_t); i; i--) {
|
||||
|
||||
/* Calculate the new scope value - re-scale using simple shifts for efficiency, re-center and y-invert */
|
||||
#if GADC_BITS_PER_SAMPLE > SCOPE_Y_BITS
|
||||
y = yoffset - (*pa++ >> (GADC_BITS_PER_SAMPLE - SCOPE_Y_BITS));
|
||||
#else
|
||||
y = yoffset - (*pa++ << (SCOPE_Y_BITS - GADC_BITS_PER_SAMPLE));
|
||||
#endif
|
||||
y = yoffset - (((coord_t)(*pa++) << shr) >> (16-SCOPE_Y_BITS));
|
||||
|
||||
#if TRIGGER_METHOD == TRIGGER_MINVALUE
|
||||
/* Calculate the scopemin ready for the next trace */
|
||||
@ -205,5 +193,7 @@ void gwinScopeWaitForTrace(GHandle gh) {
|
||||
gs->scopemin = scopemin;
|
||||
#endif
|
||||
|
||||
gfxBufferRelease(pd);
|
||||
|
||||
#undef gs
|
||||
}
|
||||
|
@ -64,9 +64,6 @@ typedef struct GScopeObject_t {
|
||||
GWindowObject g; // Base Class
|
||||
|
||||
coord_t *lastscopetrace; // To store last scope trace
|
||||
gfxSem bsem; // We get signalled on this
|
||||
adcsample_t *audiobuf; // To store audio samples
|
||||
GEventADC myEvent; // Information on received samples
|
||||
coord_t nextx; // Where we are up to
|
||||
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
|
||||
coord_t lasty; // The last y value - used for trigger slope detection
|
||||
|
@ -167,6 +167,11 @@ int main(void) {
|
||||
gtimerStart(&lsTimer, LowSpeedTimer, ghText, TRUE, MY_LS_DELAY);
|
||||
#endif
|
||||
|
||||
// Allocate buffers for the high speed GADC device - 4 x 128 byte buffers.
|
||||
// You may need to increase this for slower cpu's.
|
||||
// You may be able to decrease this for low latency operating systems.
|
||||
gfxBufferAlloc(4, 128);
|
||||
|
||||
/* Set up the scope window in the top right on the screen */
|
||||
{
|
||||
GWindowInit wi;
|
||||
|
@ -8,10 +8,6 @@
|
||||
/**
|
||||
* @file drivers/gadc/AT91SAM7/gadc_lld.c
|
||||
* @brief GADC - Periodic ADC driver source file for the AT91SAM7 cpu.
|
||||
*
|
||||
* @defgroup Driver Driver
|
||||
* @ingroup GADC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "gfx.h"
|
||||
@ -20,44 +16,76 @@
|
||||
|
||||
#include "src/gadc/driver.h"
|
||||
|
||||
static GDataBuffer *pData;
|
||||
static size_t bytesperconversion;
|
||||
|
||||
static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n);
|
||||
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err);
|
||||
|
||||
|
||||
static ADCConversionGroup acg = {
|
||||
FALSE, // circular
|
||||
1, // num_channels
|
||||
GADC_ISR_CompleteI, // end_cb
|
||||
GADC_ISR_ErrorI, // error_cb
|
||||
ISR_CompleteI, // end_cb
|
||||
ISR_ErrorI, // error_cb
|
||||
0, // channelselects
|
||||
0, // trigger
|
||||
0, // frequency
|
||||
};
|
||||
|
||||
static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
||||
(void) adcp;
|
||||
(void) buffer;
|
||||
|
||||
if (pData) {
|
||||
// A set of timer base conversions is complete
|
||||
pData->len += n * bytesperconversion;
|
||||
|
||||
// Are we finished yet?
|
||||
// In ChibiOS we (may) get a half-buffer complete. In this situation the conversions
|
||||
// are really not complete and so we just wait for the next lot of data.
|
||||
if (pData->len + bytesperconversion > pData->size)
|
||||
gadcDataReadyI();
|
||||
|
||||
} else {
|
||||
// A single non-timer conversion is complete
|
||||
gadcDataReadyI();
|
||||
}
|
||||
}
|
||||
|
||||
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
|
||||
(void) adcp;
|
||||
(void) err;
|
||||
|
||||
gadcDataFailI();
|
||||
}
|
||||
|
||||
void gadc_lld_init(void) {
|
||||
adcStart(&ADCD1, 0);
|
||||
}
|
||||
|
||||
size_t gadc_lld_samples_per_conversion(uint32_t physdev) {
|
||||
size_t cnt;
|
||||
int i;
|
||||
void gadc_lld_start_timer(GadcLldTimerData *pgtd) {
|
||||
int phys;
|
||||
|
||||
/* Calculate the bytes per conversion from physdev */
|
||||
/* The AT91SAM7 has AD0..7 - physdev is a bitmap of those channels */
|
||||
for(cnt = 0, i = 0; i < 8; i++, physdev >>= 1)
|
||||
if (physdev & 0x01)
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
phys = pgtd->physdev;
|
||||
for(bytesperconversion = 0; phys; phys >>= 1)
|
||||
if (phys & 0x01)
|
||||
bytesperconversion++;
|
||||
bytesperconversion *= (gfxSampleFormatBits(GADC_SAMPLE_FORMAT)+7)/8;
|
||||
|
||||
void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency) {
|
||||
(void) physdev;
|
||||
/**
|
||||
* The AT91SAM7 ADC driver supports triggering the ADC using a timer without having to implement
|
||||
* an interrupt handler for the timer. The driver also initialises the timer correctly for us.
|
||||
* Because we aren't trapping the interrupt ourselves we can't increment GADC_Timer_Missed if an
|
||||
* interrupt is missed.
|
||||
*/
|
||||
acg.frequency = frequency;
|
||||
acg.frequency = pgtd->frequency;
|
||||
}
|
||||
|
||||
void gadc_lld_stop_timer(uint32_t physdev) {
|
||||
(void) physdev;
|
||||
void gadc_lld_stop_timer(GadcLldTimerData *pgtd) {
|
||||
(void) pgtd;
|
||||
if ((acg.trigger & ~ADC_TRIGGER_SOFTWARE) == ADC_TRIGGER_TIMER)
|
||||
adcStop(&ADCD1);
|
||||
}
|
||||
@ -69,7 +97,8 @@ void gadc_lld_adc_timerI(GadcLldTimerData *pgtd) {
|
||||
acg.channelselects = pgtd->physdev;
|
||||
acg.trigger = pgtd->now ? (ADC_TRIGGER_TIMER|ADC_TRIGGER_SOFTWARE) : ADC_TRIGGER_TIMER;
|
||||
|
||||
adcStartConversionI(&ADCD1, &acg, pgtd->buffer, pgtd->count);
|
||||
pData = pgtd->pdata;
|
||||
adcStartConversionI(&ADCD1, &acg, (adcsample_t *)(pgtd->pdata+1), pData->size/bytesperconversion);
|
||||
|
||||
/* Next time assume the same (still running) timer */
|
||||
acg.frequency = 0;
|
||||
@ -81,8 +110,9 @@ void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd) {
|
||||
*/
|
||||
acg.channelselects = pgntd->physdev;
|
||||
acg.trigger = ADC_TRIGGER_SOFTWARE;
|
||||
|
||||
pData = 0;
|
||||
adcStartConversionI(&ADCD1, &acg, pgntd->buffer, 1);
|
||||
}
|
||||
|
||||
#endif /* GFX_USE_GADC */
|
||||
/** @} */
|
||||
|
@ -35,18 +35,13 @@
|
||||
* @note For the AT91SAM7 ADC driver, it post-dates the finding of the bug so we safely
|
||||
* say that the bug doesn't exist for this driver.
|
||||
*/
|
||||
#define ADC_ISR_FULL_CODE_BUG FALSE
|
||||
#define CHIBIOS_ADC_ISR_FULL_CODE_BUG FALSE
|
||||
|
||||
/**
|
||||
* @brief The maximum sample frequency supported by this CPU
|
||||
*/
|
||||
#define GADC_MAX_SAMPLE_FREQUENCY 132000
|
||||
|
||||
/**
|
||||
* @brief The number of bits in a sample
|
||||
*/
|
||||
#define GADC_BITS_PER_SAMPLE AT91_ADC1_RESOLUTION
|
||||
|
||||
/**
|
||||
* @brief The sample format
|
||||
*/
|
||||
|
@ -26,6 +26,12 @@
|
||||
*/
|
||||
#define GAUDIO_RECORD_NUM_CHANNELS 1
|
||||
|
||||
/**
|
||||
* @brief Whether each channel is mono or stereo
|
||||
* @note This is an example
|
||||
*/
|
||||
#define GAUDIO_RECORD_CHANNEL0_IS_STEREO FALSE
|
||||
|
||||
/**
|
||||
* @brief The list of audio channels and their uses
|
||||
* @note This is an example
|
||||
@ -40,7 +46,7 @@
|
||||
* @{
|
||||
*/
|
||||
#ifdef GAUDIO_RECORD_LLD_IMPLEMENTATION
|
||||
static uint32_t gaudin_lld_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
static uint32_t gaudio_gadc_physdevs[GAUDIO_RECORD_NUM_CHANNELS] = {
|
||||
GADC_PHYSDEV_MICROPHONE,
|
||||
};
|
||||
#endif
|
||||
|
@ -22,29 +22,21 @@
|
||||
/* Driver hardware support. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief The audio record sample type
|
||||
* @details For this driver it matches the cpu sample type
|
||||
*/
|
||||
typedef adcsample_t audio_record_sample_t;
|
||||
|
||||
/**
|
||||
* @brief The maximum sample frequency supported by this audio device
|
||||
* @details For this driver it matches the GADC maximum high speed sample rate
|
||||
*/
|
||||
#define GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY GADC_MAX_HIGH_SPEED_SAMPLERATE
|
||||
#define GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY GADC_MAX_HIGH_SPEED_SAMPLERATE
|
||||
|
||||
/**
|
||||
* @brief The number of bits in a sample
|
||||
* @details For this driver it matches the cpu sample bits
|
||||
* @brief The number of audio formats supported by this driver
|
||||
*/
|
||||
#define GAUDIO_RECORD_BITS_PER_SAMPLE GADC_BITS_PER_SAMPLE
|
||||
#define GAUDIO_RECORD_NUM_FORMATS 1
|
||||
|
||||
/**
|
||||
* @brief The format of an audio sample
|
||||
* @details For this driver it matches the cpu sample format
|
||||
* @brief The available audio sample formats in order of preference
|
||||
*/
|
||||
#define GAUDIO_RECORD_SAMPLE_FORMAT GADC_SAMPLE_FORMAT
|
||||
#define GAUDIO_RECORD_FORMAT1 GADC_SAMPLE_FORMAT
|
||||
|
||||
/**
|
||||
* For the GAUDIO driver that uses GADC - all the remaining config definitions are specific
|
||||
|
@ -8,10 +8,6 @@
|
||||
/**
|
||||
* @file drivers/gaudio/gadc/gaudio_record_lld.c
|
||||
* @brief GAUDIO - Record Driver file for using the cpu ADC (via GADC).
|
||||
*
|
||||
* @addtogroup GAUDIO
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -19,8 +15,6 @@
|
||||
* from the board definitions.
|
||||
*/
|
||||
#define GAUDIO_RECORD_IMPLEMENTATION
|
||||
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#if GFX_USE_GAUDIO && GAUDIO_NEED_RECORD
|
||||
@ -33,30 +27,38 @@
|
||||
/* Include the driver defines */
|
||||
#include "src/gaudio/driver_record.h"
|
||||
|
||||
static void gadcCallbackI(void) {
|
||||
GDataBuffer *pd;
|
||||
|
||||
pd = gadcHighSpeedGetDataI();
|
||||
if (pd)
|
||||
gaudioRecordSaveDataBlockI(pd);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
void gaudin_lld_init(const gaudin_params *paud) {
|
||||
bool_t gaudio_record_lld_init(uint16_t channel, uint32_t frequency, ArrayDataFormat format) {
|
||||
/* Check the parameters */
|
||||
if (channel >= GAUDIO_RECORD_NUM_CHANNELS || frequency > GAUDIO_RECORD_MAX_SAMPLE_FREQUENCY || format != GAUDIO_RECORD_FORMAT1)
|
||||
return FALSE;
|
||||
|
||||
/* Setup the high speed GADC */
|
||||
gadcHighSpeedInit(gaudin_lld_physdevs[paud->channel], paud->frequency, paud->buffer, paud->bufcount, paud->samplesPerEvent);
|
||||
gadcHighSpeedInit(gaudio_gadc_physdevs[channel], frequency);
|
||||
|
||||
/* Register ourselves for ISR callbacks */
|
||||
gadcHighSpeedSetISRCallback(GAUDIN_ISR_CompleteI);
|
||||
gadcHighSpeedSetISRCallback(gadcCallbackI);
|
||||
|
||||
/**
|
||||
* The gadc driver handles any errors for us by restarting the transaction so there is
|
||||
* no need for us to setup anything for GAUDIN_ISR_ErrorI()
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void gaudin_lld_start(void) {
|
||||
void gaudio_record_lld_start(void) {
|
||||
gadcHighSpeedStart();
|
||||
}
|
||||
|
||||
void gaudin_lld_stop(void) {
|
||||
void gaudio_record_lld_stop(void) {
|
||||
gadcHighSpeedStop();
|
||||
}
|
||||
|
||||
#endif /* GFX_USE_GAUDIO && GAUDIO_NEED_RECORD */
|
||||
/** @} */
|
||||
|
@ -32,9 +32,9 @@
|
||||
* @{
|
||||
*/
|
||||
typedef struct GadcLldTimerData_t {
|
||||
uint32_t physdev; /* @< A value passed to describe which physical ADC devices/channels to use. */
|
||||
adcsample_t *buffer; /* @< The static buffer to put the ADC samples into. */
|
||||
size_t count; /* @< The number of conversions to do before doing a callback and stopping the ADC. */
|
||||
uint32_t physdev; /* @< Which physical ADC devices/channels to use. Filled in by High Level Code */
|
||||
uint32_t frequency; /* @< The conversion frequency. Filled in by High Level Code */
|
||||
GDataBuffer *pdata; /* @< The buffer to put the ADC samples into. */
|
||||
bool_t now; /* @< Trigger the first conversion now rather than waiting for the first timer interrupt (if possible) */
|
||||
} GadcLldTimerData;
|
||||
/* @} */
|
||||
@ -51,30 +51,6 @@ typedef struct GadcLldNonTimerData_t {
|
||||
} GadcLldNonTimerData;
|
||||
/* @} */
|
||||
|
||||
/**
|
||||
* @brief These routines are the callbacks that the driver uses.
|
||||
* @details Defined in the high level GADC code.
|
||||
*
|
||||
* @notapi
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param[in] adcp The ADC driver
|
||||
* @param[in] buffer The sample buffer
|
||||
* @param[in] n The amount of samples
|
||||
*/
|
||||
extern void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n);
|
||||
|
||||
/**
|
||||
* @param[in] adcp The ADC driver
|
||||
* @param[in] err ADC error
|
||||
*/
|
||||
extern void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This can be incremented by the low level driver if a timer interrupt is missed.
|
||||
* @details Defined in the high level GADC code.
|
||||
@ -91,6 +67,26 @@ extern volatile bool_t GADC_Timer_Missed;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief These routines are the callbacks that the driver uses.
|
||||
* @details Defined in the high level GADC code.
|
||||
*
|
||||
* @notapi
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief The last conversion requested is now complete
|
||||
*/
|
||||
void gadcDataReadyI(void);
|
||||
|
||||
/**
|
||||
* @brief The last conversion requested failed
|
||||
*/
|
||||
void gadcDataFailI(void);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialise the driver
|
||||
*
|
||||
@ -98,64 +94,49 @@ extern "C" {
|
||||
*/
|
||||
void gadc_lld_init(void);
|
||||
|
||||
/**
|
||||
* @brief Get the number of samples in a conversion.
|
||||
* @details Calculates and returns the number of samples per conversion for the specified physdev.
|
||||
*
|
||||
* @note A physdev describing a mono device would return 1, a stereo device would return 2.
|
||||
* For most ADC's physdev is a bitmap so it is only a matter of counting the bits.
|
||||
*
|
||||
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
|
||||
*
|
||||
* @return Number of samples of the convesion
|
||||
* @api
|
||||
*/
|
||||
size_t gadc_lld_samples_per_conversion(uint32_t physdev);
|
||||
|
||||
/**
|
||||
* @brief Start a periodic timer for high frequency conversions.
|
||||
*
|
||||
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
|
||||
* @param[in] frequency The frequency to create ADC conversions
|
||||
* @param[in] pgtd The structure containing the sample frequency and physical device to use.
|
||||
*
|
||||
* @note The exact meaning of physdev is hardware dependent. It describes the channels
|
||||
* the will be used later on when a "timer" conversion is actually scheduled.
|
||||
* @note It is assumed that the timer is capable of free-running even when the ADC
|
||||
* is stopped or doing something else.
|
||||
* @details When a timer interrupt occurs a conversion should start if these is a "timer" conversion
|
||||
* @details When a timer interrupt occurs a conversion should start if there is a "timer" conversion
|
||||
* active.
|
||||
* @note If the ADC is stopped, doesn't have a "timer" conversion active or is currently executing
|
||||
* a non-timer conversion then the interrupt can be ignored other than (optionally) incrementing
|
||||
* @note Timer interrupts occurring before @p gadc_lld_adc_timerI() has been called,
|
||||
* if @p gadc_lld_adc_timerI() has been called quick enough, or while
|
||||
* a non-timer conversion is active should be ignored other than (optionally) incrementing
|
||||
* the GADC_Timer_Missed variable.
|
||||
* @note The pdata and now members of the pgtd structure are now yet valid.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gadc_lld_start_timer(uint32_t physdev, uint32_t frequency);
|
||||
void gadc_lld_start_timer(GadcLldTimerData *pgtd);
|
||||
|
||||
/**
|
||||
* @brief Stop the periodic timer for high frequency conversions.
|
||||
* @details Also stops any current "timer" conversion (but not a current "non-timer" conversion).
|
||||
*
|
||||
* @param[in] physdev A value passed to describe which physical ADC devices/channels in use.
|
||||
*
|
||||
* @note The exact meaning of physdev is hardware dependent.
|
||||
* @param[in] pgtd The structure containing the sample frequency and physical device to use.
|
||||
*
|
||||
* @note After this function returns there should be no more calls to @p gadcDataReadyI()
|
||||
* or @p gadcDataFailI() in relation to timer conversions.
|
||||
* @api
|
||||
*/
|
||||
void gadc_lld_stop_timer(uint32_t physdev);
|
||||
void gadc_lld_stop_timer(GadcLldTimerData *pgtd);
|
||||
|
||||
/**
|
||||
* @brief Start a "timer" conversion.
|
||||
* @brief Start a set of "timer" conversions.
|
||||
* @details Starts a series of conversions triggered by the timer.
|
||||
*
|
||||
* @param[in] pgtd Contains the parameters for the timer conversion.
|
||||
*
|
||||
* @note The exact meaning of physdev is hardware dependent. It is likely described in the
|
||||
* drivers gadc_lld_config.h
|
||||
* @note Some versions of ChibiOS actually call the callback function more than once, once
|
||||
* at the half-way point and once on completion. The high level code handles this.
|
||||
* @note The driver should call @p GADC_ISR_CompleteI() when it completes the operation
|
||||
* (or at the half-way point), or @p GAD_ISR_ErrorI() on an error.
|
||||
* @note The driver should call @p gadcDataReadyI() when it completes the operation
|
||||
* or @p gadcDataFailI() on an error.
|
||||
* @note The high level code ensures that this is not called while a non-timer conversion is in
|
||||
* progress
|
||||
*
|
||||
@ -171,8 +152,8 @@ void gadc_lld_adc_timerI(GadcLldTimerData *pgtd);
|
||||
*
|
||||
* @note The exact meaning of physdev is hardware dependent. It is likely described in the
|
||||
* drivers gadc_lld_config.h
|
||||
* @note The driver should call @p GADC_ISR_CompleteI() when it completes the operation
|
||||
* or @p GAD_ISR_ErrorI() on an error.
|
||||
* @note The driver should call @p gadcDataReadyI() when it completes the operation
|
||||
* or @p gadcDataFailI() on an error.
|
||||
* @note The high level code ensures that this is not called while a timer conversion is in
|
||||
* progress
|
||||
*
|
||||
|
218
src/gadc/gadc.c
218
src/gadc/gadc.c
@ -31,42 +31,30 @@
|
||||
|
||||
volatile bool_t GADC_Timer_Missed;
|
||||
|
||||
static gfxSem gadcsem;
|
||||
static gfxMutex gadcmutex;
|
||||
static GTimer LowSpeedGTimer;
|
||||
static bool_t gadcRunning;
|
||||
static gfxSem LowSpeedSlotSem;
|
||||
static gfxMutex LowSpeedMutex;
|
||||
static GTimer LowSpeedGTimer;
|
||||
static gfxQueueGSync HighSpeedBuffers;
|
||||
|
||||
#if GFX_USE_GEVENT
|
||||
static GTimer HighSpeedGTimer;
|
||||
static GTimer HighSpeedGTimer;
|
||||
#endif
|
||||
|
||||
static volatile uint16_t gflags = 0;
|
||||
#define GADC_GFLG_ISACTIVE 0x0001
|
||||
|
||||
#define GADC_FLG_ISACTIVE 0x0001
|
||||
#define GADC_FLG_ISDONE 0x0002
|
||||
#define GADC_FLG_ERROR 0x0004
|
||||
#define GADC_FLG_GTIMER 0x0008
|
||||
#define GADC_FLG_STALLED 0x0010
|
||||
|
||||
static struct hsdev {
|
||||
// Our status flags
|
||||
uint16_t flags;
|
||||
|
||||
// What we started with
|
||||
uint32_t frequency;
|
||||
adcsample_t *buffer;
|
||||
size_t bufcount;
|
||||
size_t samplesPerEvent;
|
||||
|
||||
// The last set of results
|
||||
size_t lastcount;
|
||||
adcsample_t *lastbuffer;
|
||||
uint16_t lastflags;
|
||||
|
||||
// Other stuff we need to track progress and for signaling
|
||||
GadcLldTimerData lld;
|
||||
size_t samplesPerConversion;
|
||||
size_t remaining;
|
||||
gfxSem *bsem;
|
||||
GEventADC *pEvent;
|
||||
uint16_t eventflags;
|
||||
GADCISRCallbackFunction isrfn;
|
||||
} hs;
|
||||
|
||||
@ -101,49 +89,59 @@ static inline void FindNextConversionI(void) {
|
||||
/**
|
||||
* Look for the next thing to do.
|
||||
*/
|
||||
while(curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]) {
|
||||
gadcRunning = TRUE;
|
||||
for(; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) {
|
||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) {
|
||||
gadc_lld_adc_nontimerI(&curlsdev->lld);
|
||||
return;
|
||||
}
|
||||
curlsdev++;
|
||||
}
|
||||
curlsdev = 0;
|
||||
|
||||
/* No more low speed devices - do a high speed conversion */
|
||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
||||
hs.lld.now = GADC_Timer_Missed ? TRUE : FALSE;
|
||||
GADC_Timer_Missed = 0;
|
||||
gadc_lld_adc_timerI(&hs.lld);
|
||||
return;
|
||||
hs.lld.pdata = gfxBufferGetI();
|
||||
if (hs.lld.pdata) {
|
||||
hs.lld.now = GADC_Timer_Missed || (hs.flags & GADC_FLG_STALLED);
|
||||
hs.flags &= ~GADC_FLG_STALLED;
|
||||
GADC_Timer_Missed = 0;
|
||||
gadc_lld_adc_timerI(&hs.lld);
|
||||
return;
|
||||
}
|
||||
|
||||
// Oops - no free buffers - mark stalled and go back to low speed devices
|
||||
hs.flags |= GADC_FLG_STALLED;
|
||||
hs.eventflags &= ~GADC_HSADC_RUNNING;
|
||||
for(curlsdev = ls; curlsdev < &ls[GADC_MAX_LOWSPEED_DEVICES]; curlsdev++) {
|
||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE) {
|
||||
gadc_lld_adc_nontimerI(&curlsdev->lld);
|
||||
return;
|
||||
}
|
||||
}
|
||||
curlsdev = 0;
|
||||
}
|
||||
|
||||
/* Nothing more to do */
|
||||
gflags &= ~GADC_GFLG_ISACTIVE;
|
||||
gadcRunning = FALSE;
|
||||
}
|
||||
|
||||
void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
||||
(void) adcp;
|
||||
void gadcDataReadyI(void) {
|
||||
|
||||
if (curlsdev) {
|
||||
/* This interrupt must be in relation to the low speed device */
|
||||
|
||||
if (curlsdev->flags & GADC_FLG_ISACTIVE) {
|
||||
/**
|
||||
* As we only handle a single low speed conversion at a time, we know
|
||||
* we know we won't get any half completion interrupts.
|
||||
*/
|
||||
curlsdev->flags |= GADC_FLG_ISDONE;
|
||||
gtimerJabI(&LowSpeedGTimer);
|
||||
}
|
||||
|
||||
#if ADC_ISR_FULL_CODE_BUG
|
||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
||||
/**
|
||||
* Oops - We have just finished a low speed conversion but a bug prevents us
|
||||
* restarting the ADC here. Other code will restart it in the thread based
|
||||
* ADC handler.
|
||||
*/
|
||||
gflags &= ~GADC_GFLG_ISACTIVE;
|
||||
gadcRunning = FALSE;
|
||||
return;
|
||||
|
||||
#endif
|
||||
@ -152,49 +150,31 @@ void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
||||
/* This interrupt must be in relation to the high speed device */
|
||||
|
||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
||||
/* Save the details */
|
||||
hs.lastcount = n;
|
||||
hs.lastbuffer = buffer;
|
||||
hs.lastflags = GADC_Timer_Missed ? GADC_HSADC_LOSTEVENT : 0;
|
||||
if (hs.lld.pdata->len) {
|
||||
/* Save the current buffer on the HighSpeedBuffers */
|
||||
gfxQueueGSyncPutI(&HighSpeedBuffers, (gfxQueueGSyncItem *)hs.lld.pdata);
|
||||
hs.lld.pdata = 0;
|
||||
|
||||
/* Save the details */
|
||||
hs.eventflags = GADC_HSADC_RUNNING|GADC_HSADC_GOTBUFFER;
|
||||
if (GADC_Timer_Missed)
|
||||
hs.eventflags |= GADC_HSADC_LOSTEVENT;
|
||||
if (hs.flags & GADC_FLG_STALLED)
|
||||
hs.eventflags |= GADC_HSADC_STALL;
|
||||
|
||||
/* Our signalling mechanisms */
|
||||
if (hs.isrfn)
|
||||
hs.isrfn();
|
||||
|
||||
/* Signal the user with the data */
|
||||
if (hs.pEvent) {
|
||||
#if GFX_USE_GEVENT
|
||||
hs.pEvent->type = GEVENT_ADC;
|
||||
if (hs.flags & GADC_FLG_GTIMER)
|
||||
gtimerJabI(&HighSpeedGTimer);
|
||||
#endif
|
||||
hs.pEvent->count = hs.lastcount;
|
||||
hs.pEvent->buffer = hs.lastbuffer;
|
||||
hs.pEvent->flags = hs.lastflags;
|
||||
}
|
||||
|
||||
/* Our three signalling mechanisms */
|
||||
if (hs.isrfn)
|
||||
hs.isrfn(buffer, n);
|
||||
|
||||
if (hs.bsem)
|
||||
gfxSemSignalI(hs.bsem);
|
||||
|
||||
#if GFX_USE_GEVENT
|
||||
if (hs.flags & GADC_FLG_GTIMER)
|
||||
gtimerJabI(&HighSpeedGTimer);
|
||||
#endif
|
||||
|
||||
/* Adjust what we have left to do */
|
||||
hs.lld.count -= n;
|
||||
hs.remaining -= n;
|
||||
|
||||
/* Half completion - We have done all we can for now - wait for the next interrupt */
|
||||
if (hs.lld.count)
|
||||
return;
|
||||
|
||||
/* Our buffer is cyclic - set up the new buffer pointers */
|
||||
if (hs.remaining) {
|
||||
hs.lld.buffer = buffer + (n * hs.samplesPerConversion);
|
||||
} else {
|
||||
hs.remaining = hs.bufcount;
|
||||
hs.lld.buffer = hs.buffer;
|
||||
// Oops - no data in this buffer. Just return it to the free-list
|
||||
gfxBufferRelease(hs.lld.pdata);
|
||||
hs.lld.pdata = 0;
|
||||
}
|
||||
hs.lld.count = hs.remaining < hs.samplesPerEvent ? hs.remaining : hs.samplesPerEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,22 +184,19 @@ void GADC_ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
||||
FindNextConversionI();
|
||||
}
|
||||
|
||||
void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
|
||||
(void) adcp;
|
||||
(void) err;
|
||||
|
||||
void gadcDataFailI(void) {
|
||||
if (curlsdev) {
|
||||
if ((curlsdev->flags & (GADC_FLG_ISACTIVE|GADC_FLG_ISDONE)) == GADC_FLG_ISACTIVE)
|
||||
/* Mark the error then try to repeat it */
|
||||
curlsdev->flags |= GADC_FLG_ERROR;
|
||||
|
||||
#if ADC_ISR_FULL_CODE_BUG
|
||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
||||
/**
|
||||
* Oops - We have just finished a low speed conversion but a bug prevents us
|
||||
* restarting the ADC here. Other code will restart it in the thread based
|
||||
* ADC handler.
|
||||
*/
|
||||
gflags &= ~GADC_GFLG_ISACTIVE;
|
||||
gadcRunning = FALSE;
|
||||
gtimerJabI(&LowSpeedGTimer);
|
||||
return;
|
||||
|
||||
@ -239,8 +216,9 @@ void GADC_ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
|
||||
void _gadcInit(void)
|
||||
{
|
||||
gadc_lld_init();
|
||||
gfxSemInit(&gadcsem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES);
|
||||
gfxMutexInit(&gadcmutex);
|
||||
gfxQueueGSyncInit(&HighSpeedBuffers);
|
||||
gfxSemInit(&LowSpeedSlotSem, GADC_MAX_LOWSPEED_DEVICES, GADC_MAX_LOWSPEED_DEVICES);
|
||||
gfxMutexInit(&LowSpeedMutex);
|
||||
gtimerInit(&LowSpeedGTimer);
|
||||
#if GFX_USE_GEVENT
|
||||
gtimerInit(&HighSpeedGTimer);
|
||||
@ -252,8 +230,9 @@ void _gadcDeinit(void)
|
||||
/* commented stuff is ToDo */
|
||||
|
||||
// gadc_lld_deinit();
|
||||
gfxSemDestroy(&gadcsem);
|
||||
gfxMutexDestroy(&gadcmutex);
|
||||
gfxQueueGSyncDeinit(&HighSpeedBuffers);
|
||||
gfxSemDestroy(&LowSpeedSlotSem);
|
||||
gfxMutexDestroy(&LowSpeedMutex);
|
||||
gtimerDeinit(&LowSpeedGTimer);
|
||||
#if GFX_USE_GEVENT
|
||||
gtimerDeinit(&HighSpeedGTimer);
|
||||
@ -262,7 +241,7 @@ void _gadcDeinit(void)
|
||||
|
||||
static inline void StartADC(bool_t onNoHS) {
|
||||
gfxSystemLock();
|
||||
if (!(gflags & GADC_GFLG_ISACTIVE) || (onNoHS && !curlsdev))
|
||||
if (!gadcRunning || (onNoHS && !curlsdev))
|
||||
FindNextConversionI();
|
||||
gfxSystemUnlock();
|
||||
}
|
||||
@ -289,9 +268,7 @@ static void BSemSignalCallback(adcsample_t *buffer, void *param) {
|
||||
}
|
||||
|
||||
pe->type = GEVENT_ADC;
|
||||
pe->count = hs.lastcount;
|
||||
pe->buffer = hs.lastbuffer;
|
||||
pe->flags = hs.lastflags | psl->srcflags;
|
||||
pe->flags = hs.eventflags | psl->srcflags;
|
||||
psl->srcflags = 0;
|
||||
geventSendEvent(psl);
|
||||
}
|
||||
@ -305,7 +282,7 @@ static void LowSpeedGTimerCallback(void *param) {
|
||||
adcsample_t *buffer;
|
||||
struct lsdev *p;
|
||||
|
||||
#if ADC_ISR_FULL_CODE_BUG
|
||||
#if GFX_USE_OS_CHIBIOS && CHIBIOS_ADC_ISR_FULL_CODE_BUG
|
||||
/* Ensure the ADC is running if it needs to be - Bugfix HACK */
|
||||
StartADC(FALSE);
|
||||
#endif
|
||||
@ -325,33 +302,22 @@ static void LowSpeedGTimerCallback(void *param) {
|
||||
p->param = 0; // Needed to prevent the compiler removing the local variables
|
||||
p->lld.buffer = 0; // Needed to prevent the compiler removing the local variables
|
||||
p->flags = 0; // The slot is available (indivisible operation)
|
||||
gfxSemSignal(&gadcsem); // Tell everyone
|
||||
gfxSemSignal(&LowSpeedSlotSem); // Tell everyone
|
||||
fn(buffer, prm); // Perform the callback
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent)
|
||||
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency)
|
||||
{
|
||||
gadcHighSpeedStop(); /* This does the init for us */
|
||||
|
||||
/* Just save the details and reset everything for now */
|
||||
hs.frequency = frequency;
|
||||
hs.buffer = buffer;
|
||||
hs.bufcount = bufcount;
|
||||
hs.samplesPerEvent = samplesPerEvent;
|
||||
hs.lastcount = 0;
|
||||
hs.lastbuffer = 0;
|
||||
hs.lastflags = 0;
|
||||
hs.lld.physdev = physdev;
|
||||
hs.lld.buffer = buffer;
|
||||
hs.lld.count = samplesPerEvent;
|
||||
hs.lld.frequency = frequency;
|
||||
hs.lld.pdata = 0;
|
||||
hs.lld.now = FALSE;
|
||||
hs.samplesPerConversion = gadc_lld_samples_per_conversion(physdev);
|
||||
hs.remaining = bufcount;
|
||||
hs.bsem = 0;
|
||||
hs.pEvent = 0;
|
||||
hs.isrfn = 0;
|
||||
}
|
||||
|
||||
@ -368,12 +334,12 @@ void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn) {
|
||||
hs.isrfn = isrfn;
|
||||
}
|
||||
|
||||
void gadcHighSpeedSetBSem(gfxSem *pbsem, GEventADC *pEvent) {
|
||||
/* Use the system lock to ensure they occur atomically */
|
||||
gfxSystemLock();
|
||||
hs.pEvent = pEvent;
|
||||
hs.bsem = pbsem;
|
||||
gfxSystemUnlock();
|
||||
GDataBuffer *gadcHighSpeedGetData(delaytime_t ms) {
|
||||
return (GDataBuffer *)gfxQueueGSyncGet(&HighSpeedBuffers, ms);
|
||||
}
|
||||
|
||||
GDataBuffer *gadcHighSpeedGetDataI(void) {
|
||||
return (GDataBuffer *)gfxQueueGSyncGetI(&HighSpeedBuffers);
|
||||
}
|
||||
|
||||
void gadcHighSpeedStart(void) {
|
||||
@ -381,8 +347,8 @@ void gadcHighSpeedStart(void) {
|
||||
if (hs.flags & GADC_FLG_ISACTIVE)
|
||||
return;
|
||||
|
||||
gadc_lld_start_timer(hs.lld.physdev, hs.frequency);
|
||||
hs.flags = GADC_FLG_ISACTIVE;
|
||||
gadc_lld_start_timer(&hs.lld);
|
||||
StartADC(FALSE);
|
||||
}
|
||||
|
||||
@ -390,7 +356,15 @@ void gadcHighSpeedStop(void) {
|
||||
if (hs.flags & GADC_FLG_ISACTIVE) {
|
||||
/* No more from us */
|
||||
hs.flags = 0;
|
||||
gadc_lld_stop_timer(hs.lld.physdev);
|
||||
gadc_lld_stop_timer(&hs.lld);
|
||||
/*
|
||||
* There might be a buffer still locked up by the driver - if so release it.
|
||||
*/
|
||||
if (hs.lld.pdata) {
|
||||
gfxBufferRelease(hs.lld.pdata);
|
||||
hs.lld.pdata = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to pass TRUE to StartADC() as we might have the ADC marked as active when it isn't
|
||||
* due to stopping the timer while it was converting.
|
||||
@ -405,17 +379,17 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) {
|
||||
|
||||
/* Start the Low Speed Timer */
|
||||
gfxSemInit(&mysem, 1, 1);
|
||||
gfxMutexEnter(&gadcmutex);
|
||||
gfxMutexEnter(&LowSpeedMutex);
|
||||
if (!gtimerIsActive(&LowSpeedGTimer))
|
||||
gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
||||
gfxMutexExit(&gadcmutex);
|
||||
gfxMutexExit(&LowSpeedMutex);
|
||||
|
||||
while(1) {
|
||||
/* Wait for an available slot */
|
||||
gfxSemWait(&gadcsem, TIME_INFINITE);
|
||||
gfxSemWait(&LowSpeedSlotSem, TIME_INFINITE);
|
||||
|
||||
/* Find a slot */
|
||||
gfxMutexEnter(&gadcmutex);
|
||||
gfxMutexEnter(&LowSpeedMutex);
|
||||
for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) {
|
||||
if (!(p->flags & GADC_FLG_ISACTIVE)) {
|
||||
p->lld.physdev = physdev;
|
||||
@ -423,13 +397,13 @@ void gadcLowSpeedGet(uint32_t physdev, adcsample_t *buffer) {
|
||||
p->fn = BSemSignalCallback;
|
||||
p->param = &mysem;
|
||||
p->flags = GADC_FLG_ISACTIVE;
|
||||
gfxMutexExit(&gadcmutex);
|
||||
gfxMutexExit(&LowSpeedMutex);
|
||||
StartADC(FALSE);
|
||||
gfxSemWait(&mysem, TIME_INFINITE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gfxMutexExit(&gadcmutex);
|
||||
gfxMutexExit(&LowSpeedMutex);
|
||||
|
||||
/**
|
||||
* We should never get here - the count semaphore must be wrong.
|
||||
@ -442,7 +416,7 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc
|
||||
struct lsdev *p;
|
||||
|
||||
/* Start the Low Speed Timer */
|
||||
gfxMutexEnter(&gadcmutex);
|
||||
gfxMutexEnter(&LowSpeedMutex);
|
||||
if (!gtimerIsActive(&LowSpeedGTimer))
|
||||
gtimerStart(&LowSpeedGTimer, LowSpeedGTimerCallback, 0, TRUE, TIME_INFINITE);
|
||||
|
||||
@ -450,18 +424,18 @@ bool_t gadcLowSpeedStart(uint32_t physdev, adcsample_t *buffer, GADCCallbackFunc
|
||||
for(p = ls; p < &ls[GADC_MAX_LOWSPEED_DEVICES]; p++) {
|
||||
if (!(p->flags & GADC_FLG_ISACTIVE)) {
|
||||
/* We know we have a slot - this should never wait anyway */
|
||||
gfxSemWait(&gadcsem, TIME_IMMEDIATE);
|
||||
gfxSemWait(&LowSpeedSlotSem, TIME_IMMEDIATE);
|
||||
p->lld.physdev = physdev;
|
||||
p->lld.buffer = buffer;
|
||||
p->fn = fn;
|
||||
p->param = param;
|
||||
p->flags = GADC_FLG_ISACTIVE;
|
||||
gfxMutexExit(&gadcmutex);
|
||||
gfxMutexExit(&LowSpeedMutex);
|
||||
StartADC(FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
gfxMutexExit(&gadcmutex);
|
||||
gfxMutexExit(&LowSpeedMutex);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -73,15 +73,10 @@ typedef struct GEventADC_t {
|
||||
* @{
|
||||
*/
|
||||
#define GADC_HSADC_LOSTEVENT 0x0001 /**< @brief The last GEVENT_HSDADC event was lost */
|
||||
#define GADC_HSADC_RUNNING 0x0002 /**< @brief The High Speed ADC is currently running */
|
||||
#define GADC_HSADC_GOTBUFFER 0x0004 /**< @brief A buffer is ready for processing */
|
||||
#define GADC_HSADC_STALL 0x0008 /**< @brief The High Speed ADC has stalled due to no free buffers */
|
||||
/** @} */
|
||||
/**
|
||||
* @brief The number of conversions in the buffer
|
||||
*/
|
||||
size_t count;
|
||||
/**
|
||||
* @brief The buffer containing the conversion samples
|
||||
*/
|
||||
adcsample_t *buffer;
|
||||
} GEventADC;
|
||||
/** @} */
|
||||
|
||||
@ -93,7 +88,7 @@ typedef void (*GADCCallbackFunction)(adcsample_t *buffer, void *param);
|
||||
/**
|
||||
* @brief A callback function (executed in an ISR context) for a high speed conversion
|
||||
*/
|
||||
typedef void (*GADCISRCallbackFunction)(adcsample_t *buffer, size_t size);
|
||||
typedef void (*GADCISRCallbackFunction)(void);
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
@ -109,40 +104,28 @@ extern "C" {
|
||||
*
|
||||
* @param[in] physdev A value passed to describe which physical ADC devices/channels to use.
|
||||
* @param[in] frequency The frequency to create ADC conversions
|
||||
* @param[in] buffer The static buffer to put the ADC samples into.
|
||||
* @param[in] bufcount The total number of conversions that will fit in the buffer.
|
||||
* @param[in] samplesPerEvent The number of conversions to do before returning an event.
|
||||
*
|
||||
* @note If the high speed ADC is running it will be stopped. The Event subsystem is
|
||||
* disconnected from the high speed ADC and any binary semaphore event is forgotten.
|
||||
* @note bufcount must be greater than countPerEvent (usually 2 or more times) otherwise
|
||||
* the buffer will be overwritten with new data while the application is still trying
|
||||
* to process the old data.
|
||||
* @note Due to a bug/feature in Chibi-OS countPerEvent must be even. If bufcount is not
|
||||
* evenly divisable by countPerEvent, the remainder must also be even.
|
||||
* @note ChibiOS ONLY: Due to a bug in ChibiOS each buffer on the free-list must contain an even number of
|
||||
* samples and for multi-channel devices it must hold a number of samples that is evenly divisible
|
||||
* by 2 times the number of active channels.
|
||||
* @note The physdev parameter may be used to turn on more than one ADC channel.
|
||||
* Each channel is then interleaved into the provided buffer. Note 'bufcount'
|
||||
* and 'countPerEvent' parameters describe the number of conversions not the
|
||||
* number of samples.
|
||||
* Each channel is then interleaved into the provided buffer. Make sure your buffers all hold
|
||||
* a number of samples evenly divisible by the number of active channels.
|
||||
* As an example, if physdev turns on 2 devices then the buffer contains
|
||||
* alternate device samples and the buffer must contain 2 * bufcount samples.
|
||||
* alternate device samples and the buffer must contain multiples of 2 samples.
|
||||
* The exact meaning of physdev is hardware dependent.
|
||||
* @note The buffer is circular. When the end of the buffer is reached it will start
|
||||
* putting data into the beginning of the buffer again.
|
||||
* @note The event listener must process the event (and the data in it) before the
|
||||
* next event occurs. If not, the following event will be lost.
|
||||
* @note If bufcount is evenly divisable by countPerEvent, then every event will return
|
||||
* countPerEvent conversions. If bufcount is not evenly divisable, it will return
|
||||
* a block of samples containing less than countPerEvent samples when it reaches the
|
||||
* end of the buffer.
|
||||
* @note While the high speed ADC is running, low speed conversions can only occur at
|
||||
* the frequency of the high speed events. Thus if high speed events are
|
||||
* being created at 50Hz (eg countPerEvent = 100, frequency = 5kHz) then the maximum
|
||||
* being created at 50Hz (eg 100 samples/buffer, frequency = 5kHz) then the maximum
|
||||
* frequency for low speed conversions will be 50Hz.
|
||||
* @note Only a single sample format is supported - that provided by the GADC driver. That sample
|
||||
* format applies to both high speed and low speed sampling.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer, size_t bufcount, size_t samplesPerEvent);
|
||||
void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency);
|
||||
|
||||
#if GFX_USE_GEVENT || defined(__DOXYGEN__)
|
||||
/**
|
||||
@ -170,7 +153,7 @@ void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer
|
||||
*
|
||||
* @note Passing a NULL for isrfn will turn off signalling via this method as will calling
|
||||
* @p gadcHighSpeedInit().
|
||||
* @note The high speed ADC is capable of signalling via this method, a binary semaphore and the GEVENT
|
||||
* @note The high speed ADC is capable of signalling via this method, a blocked thread and the GEVENT
|
||||
* sub-system at the same time.
|
||||
*
|
||||
* @api
|
||||
@ -178,19 +161,24 @@ void gadcHighSpeedInit(uint32_t physdev, uint32_t frequency, adcsample_t *buffer
|
||||
void gadcHighSpeedSetISRCallback(GADCISRCallbackFunction isrfn);
|
||||
|
||||
/**
|
||||
* @brief Allow retrieving of results from the high speed ADC using a Binary Semaphore and a static event buffer.
|
||||
* @brief Get a filled buffer from the ADC
|
||||
* @return A GDataBuffer pointer or NULL if the timeout is exceeded
|
||||
*
|
||||
* @param[in] pbsem The semaphore is signaled when data is available.
|
||||
* @param[in] pEvent The static event buffer to place the result information.
|
||||
*
|
||||
* @note Passing a NULL for pbsem or pEvent will turn off signalling via this method as will calling
|
||||
* @p gadcHighSpeedInit().
|
||||
* @note The high speed ADC is capable of signalling via this method, an ISR callback and the GEVENT
|
||||
* sub-system at the same time.
|
||||
* @params[in] ms The maximum amount of time in milliseconds to wait for data if some is not currently available.
|
||||
*
|
||||
* @note After processing the data, your application must return the buffer to the free-list so that
|
||||
* it can be used again. This can be done using @p gfxBufferRelease().
|
||||
* @note A buffer may be returned to the free-list before you have finished processing it provided you finish
|
||||
* processing it before GADC re-uses it. This is useful when RAM usage is critical to reduce the number
|
||||
* of buffers required. It works before the free list is a FIFO queue and therefore buffers are kept
|
||||
* in the queue as long as possible before they are re-used.
|
||||
* @note The function ending with "I" is the interrupt class function.
|
||||
* @api
|
||||
* @{
|
||||
*/
|
||||
void gadcHighSpeedSetBSem(gfxSem *pbsem, GEventADC *pEvent);
|
||||
GDataBuffer *gadcHighSpeedGetData(delaytime_t ms);
|
||||
GDataBuffer *gadcHighSpeedGetDataI(void);
|
||||
/* @} */
|
||||
|
||||
/**
|
||||
* @brief Start the high speed ADC conversions.
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user