1
0
mirror of https://github.com/elua/elua.git synced 2025-01-08 20:56:17 +08:00

Updated ADC Module:

- now uses bitfields rather than HWREGBITW for keeping track of state
- started generalizing functionality for smoothing, state tracking etc... into
  separate functions
- burst mode now works, including with smoothing enabled (only operates in a
  blocking mode, non-blocking to come later)
This commit is contained in:
James Snyder 2009-01-31 21:04:08 +00:00
parent afa96dcc25
commit 709d18c080
3 changed files with 214 additions and 119 deletions

View File

@ -178,16 +178,6 @@ u32 platform_cpu_get_frequency();
// The platform ADC functions // The platform ADC functions
int platform_adc_exists( unsigned id ); int platform_adc_exists( unsigned id );
enum
{
PLATFORM_ADC_CH_SETUP, // Have we set up the sequence?
PLATFORM_ADC_CH_PENDING, // Is there a pending conversion?
PLATFORM_ADC_CH_NONBLOCKING, // Are we in blocking or non-blocking mode? (0 - blocking, 1 - nonblocking)
PLATFORM_ADC_CH_BURST, // Acquiring in burst mode
PLATFORM_ADC_CH_DATA_READY // Is data ready for this channel
};
u32 platform_adc_op( unsigned id, int op, u32 data ); u32 platform_adc_op( unsigned id, int op, u32 data );
enum enum
@ -206,14 +196,20 @@ enum
PLATFORM_ADC_NONBLOCKING PLATFORM_ADC_NONBLOCKING
}; };
// Platform ADC state // Platform ADC state
struct platform_adc_state struct platform_adc_state
{ {
u8 status; volatile u8 op_pending: 1, // Is there a pending conversion?
u8 burstbuffersz, burstbufferidx; nonblocking: 1, // Are we in blocking or non-blocking mode? (0 - blocking, 1 - nonblocking)
u8 smoothbuffsz, smoothbuffidx; burst: 1, // Acquiring in burst mode
unsigned long smoothingav, smoothingsum; data_ready: 1, // Is data ready for this channel
u16 *burstbuff, *smoothbuff; smooth_ready: 1; // Has smoothing filter warmed up (i.e. smoothlen samples collected)
unsigned id, timer_id;
u8 burstlen, burstidx;
u8 smoothlen, smoothidx;
unsigned long smoothavg, smoothsum;
u16 sample, *burstbuf, *smoothbuf;
}; };
void platform_adc_set_mode( unsigned id, int mode ); void platform_adc_set_mode( unsigned id, int mode );

View File

@ -7,6 +7,7 @@
#include "auxmods.h" #include "auxmods.h"
#include "lrotable.h" #include "lrotable.h"
#include "platform_conf.h" #include "platform_conf.h"
#include <stdlib.h> // needed for malloc
// Lua: sample( id ) // Lua: sample( id )
static int adc_sample( lua_State* L ) static int adc_sample( lua_State* L )
@ -87,10 +88,42 @@ static int adc_get_smoothing( lua_State* L)
return 1; return 1;
} }
// Lua: adc_burst(id, buffer) // Lua: burst( id, count, timer_id, frequency )
//static int adc_burst( ) static int adc_burst( lua_State* L )
//void platform_adc_burst( unsigned id, u16* buf, unsigned count, unsigned timer_id, u32 frequency ) {
unsigned i, id, count, timer_id;
u32 frequency;
u16 *buf;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( adc, id );
count = luaL_checkinteger( L, 2 );
timer_id = luaL_checkinteger( L, 3 );
MOD_CHECK_ID( timer, timer_id );
frequency = luaL_checkinteger( L, 4 );
// Allocate buffer to contain returned samples
if( ( buf = malloc( count * sizeof( u16 ) ) ) == NULL )
return luaL_error( L, "Buffer allocation failed." );
for( i = 0; i < count; i ++ )
buf[ i ] = 0;
platform_adc_burst( id, buf, count, timer_id, frequency );
// Push data back to Lua
lua_createtable( L, count, 0 );
for( i = 0; i < count; i ++ )
{
lua_pushinteger( L, buf[ i ] );
lua_rawseti( L, -2, i+1 );
}
// Free buffer
free( buf );
return 1;
}
// Module function map // Module function map
#define MIN_OPT_LEVEL 2 #define MIN_OPT_LEVEL 2
@ -103,6 +136,7 @@ const LUA_REG_TYPE adc_map[] =
{ LSTRKEY( "setmode" ), LFUNCVAL( adc_set_mode ) }, { LSTRKEY( "setmode" ), LFUNCVAL( adc_set_mode ) },
{ LSTRKEY( "setsmoothing" ), LFUNCVAL( adc_set_smoothing ) }, { LSTRKEY( "setsmoothing" ), LFUNCVAL( adc_set_smoothing ) },
{ LSTRKEY( "getsmoothing" ), LFUNCVAL( adc_get_smoothing ) }, { LSTRKEY( "getsmoothing" ), LFUNCVAL( adc_get_smoothing ) },
{ LSTRKEY( "burst" ), LFUNCVAL( adc_burst ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };

View File

@ -480,16 +480,132 @@ void platform_cpu_disable_interrupts()
// ***************************************************************************** // *****************************************************************************
// ADC specific functions and variables // ADC specific functions and variables
// NOTES: HWREGBITW should be replaced with some similar function that will work for other platforms
const static u32 adc_ctls[] = { ADC_CTL_CH0, ADC_CTL_CH1, ADC_CTL_CH2, ADC_CTL_CH3 }; const static u32 adc_ctls[] = { ADC_CTL_CH0, ADC_CTL_CH1, ADC_CTL_CH2, ADC_CTL_CH3 };
const static u32 adc_ints[] = { INT_ADC0, INT_ADC1, INT_ADC2, INT_ADC3 }; const static u32 adc_ints[] = { INT_ADC0, INT_ADC1, INT_ADC2, INT_ADC3 };
struct platform_adc_state adc_state[ NUM_ADC ]; struct platform_adc_state adc_state[ NUM_ADC ];
void platform_adc_stop_burst( struct platform_adc_state *s )
{
ADCSequenceDisable( ADC_BASE, s->id );
TimerControlTrigger( timer_base[s->timer_id], TIMER_A, false );
}
// Initialize Configuration and Buffers
void adc_init_state( struct platform_adc_state *s, unsigned id )
{
// Initialize Configuration
s->op_pending = 0;
s->nonblocking = 0;
s->burst = 0;
s->data_ready = 0;
s->smooth_ready = 0;
s->id = id;
s->timer_id = 0;
s->burstlen = 1;
s->burstidx = 0;
s->smoothlen = 1;
s->smoothidx = 0;
s->sample = 0;
// Data Configuration
s->smoothavg = 0;
s->smoothsum = 0;
// Buffer initialization
s->smoothbuf = malloc( s->smoothlen * sizeof( u16 ) );
}
int adc_update_smoothing(struct platform_adc_state *s, u8 len)
{
u8 i;
if( len != s->smoothlen )
{
s->smoothlen = len;
// Free old buffer space
if ( s->smoothbuf != NULL )
free( s->smoothbuf );
// Reset sum, avg, index location
s->smoothidx = 0;
s->smoothavg = 0;
s->smoothsum = 0;
s->smooth_ready = 0;
// Allocate and zero new smoothing buffer
if( ( s->smoothbuf = malloc( s->smoothlen * sizeof( u16 ) ) ) == NULL )
{
return 1;
}
for( i = 0; i < s->smoothlen; i ++ )
s->smoothbuf[ i ] = 0;
}
return 0;
}
void adc_process_data( struct platform_adc_state *s, u16 samplevalue )
{
// Take just acquired sample and do smoothing / burst buffering
if ( s->smoothlen > 1 )
{
if( s->smoothidx == s->smoothlen )
{
s->smoothidx = 0;
s->smooth_ready = 1;
}
// Subtract Oldest Value from Sum
s->smoothsum -= s->smoothbuf[ s->smoothidx ];
// Replace Oldest Value in Buffer
s->smoothbuf[ s->smoothidx ] = ( u16 ) samplevalue;
// Add New Sample to Sum
s->smoothsum += s->smoothbuf[ s->smoothidx ];
s->smoothidx++;
// Calculate Average
if ( ( s->smoothlen != 0 ) && !( s->smoothlen & ( s->smoothlen - 1 ) ) )
s->smoothavg = s->smoothsum >> intlog2( s->smoothlen );
else
s->smoothavg = s->smoothsum / s->smoothlen;
s->sample = ( u16 ) s->smoothavg;
}
else
s->sample = samplevalue;
// Increment buffer position, unless we're waiting on the smoothing filter to warm up
if ( ( ( s->smooth_ready == 1 && s->smoothlen > 1 ) || s->smoothlen == 1 ) && ( s->burst == 1 ) )
s->burstbuf[ s->burstidx++ ] = s->sample;
// If we have enough samples, clean up / finish
if ( s->burstidx == s->burstlen || s->burst == 0 )
{
s->data_ready = 1;
s->op_pending = 0;
if ( s->burst == 1 )
{
// Disable Burst Mode
platform_adc_stop_burst( s );
s->burst = 0;
}
}
}
// Handle ADC interrupts // Handle ADC interrupts
void ADCIntHandler( void ) void ADCIntHandler( void )
{ {
unsigned long samplevalue; unsigned long rawSample;
unsigned id; unsigned id;
// Check each sequence for a pending sample // Check each sequence for a pending sample
@ -497,46 +613,12 @@ void ADCIntHandler( void )
{ {
if( ADCIntStatus(ADC_BASE, id, false) ) if( ADCIntStatus(ADC_BASE, id, false) )
{ {
// Clear Interrupt & Get Sample
ADCIntClear(ADC_BASE, id); ADCIntClear(ADC_BASE, id);
ADCSequenceDataGet(ADC_BASE, id, &rawSample);
// Get samples // Process Received Sample
ADCSequenceDataGet(ADC_BASE, id, &samplevalue); adc_process_data(&adc_state[ id ], (u16) rawSample);
// Smooth data if needed
if ( adc_state[id].smoothbuffsz > 1 )
{
if( adc_state[id].smoothbuffidx == adc_state[id].smoothbuffsz )
{
adc_state[id].smoothbuffidx = 0;
}
// Subtract Oldest Value from Sum
adc_state[id].smoothingsum -= adc_state[id].smoothbuff[adc_state[id].smoothbuffidx];
// Replace Oldest Value in Buffer
adc_state[id].smoothbuff[adc_state[id].smoothbuffidx] = (u16) samplevalue;
// Add New Sample to Sum
adc_state[id].smoothingsum += adc_state[id].smoothbuff[adc_state[id].smoothbuffidx];
adc_state[id].smoothbuffidx++;
// Calculate Average
if ( (adc_state[id].smoothbuffsz != 0) && \
!(adc_state[id].smoothbuffsz & (adc_state[id].smoothbuffsz - 1)) )
adc_state[id].smoothingav = adc_state[id].smoothingsum >> intlog2( adc_state[id].smoothbuffsz );
else
adc_state[id].smoothingav = adc_state[id].smoothingsum / adc_state[id].smoothbuffsz;
adc_state[id].burstbuff[0] = (u16) adc_state[id].smoothingav;
}
else
adc_state[id].burstbuff[0] = (u16) samplevalue;
// Mark channel as no longer having a fresh sample, close pending state
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_DATA_READY ) = 1;
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_PENDING ) = 0;
} }
} }
} }
@ -544,22 +626,13 @@ void ADCIntHandler( void )
static void adcs_init() static void adcs_init()
{ {
unsigned id; unsigned id;
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC); SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC);
for( id = 0; id < NUM_ADC; id ++ ) for( id = 0; id < NUM_ADC; id ++ )
{ {
adc_state[id].status = 0x00; // Init ADC State Struct
adc_init_state( &adc_state[ id ], id );
adc_state[id].burstbuffersz = 1;
adc_state[id].burstbufferidx = 0;
adc_state[id].smoothbuffsz = 1;
adc_state[id].smoothbuffidx = 0;
adc_state[id].smoothingav = 0;
adc_state[id].smoothingsum = 0;
adc_state[id].burstbuff = malloc( adc_state[id].burstbuffersz * sizeof( u16 ) );
adc_state[id].smoothbuff = malloc( adc_state[id].smoothbuffsz * sizeof( u16 ) );
// Make sure sequencer is disabled before making changes // Make sure sequencer is disabled before making changes
ADCSequenceDisable( ADC_BASE, id ); ADCSequenceDisable( ADC_BASE, id );
@ -567,8 +640,6 @@ static void adcs_init()
// Conversion initiated on processor trigger // Conversion initiated on processor trigger
ADCSequenceConfigure( ADC_BASE, id, ADC_TRIGGER_PROCESSOR, id ) ; ADCSequenceConfigure( ADC_BASE, id, ADC_TRIGGER_PROCESSOR, id ) ;
// ADC_CTL_IE causes an interrupt to be fired when this step is complete
// ADC_CTL_END causes this to be the last step to be taken in the sequence
// Samples go into sequencer of the same number as input channel // Samples go into sequencer of the same number as input channel
ADCSequenceStepConfigure( ADC_BASE, id, 0, ADC_CTL_IE | ADC_CTL_END | adc_ctls[id] ); ADCSequenceStepConfigure( ADC_BASE, id, 0, ADC_CTL_IE | ADC_CTL_END | adc_ctls[id] );
@ -584,7 +655,6 @@ u32 platform_adc_op( unsigned id, int op, u32 data ) // Move to common?
{ {
// Do we really need 32-bit in and out? // Do we really need 32-bit in and out?
u32 res = 0; u32 res = 0;
u8 ctr = 0;
switch( op ) switch( op )
{ {
@ -593,32 +663,12 @@ u32 platform_adc_op( unsigned id, int op, u32 data ) // Move to common?
break; break;
case PLATFORM_ADC_GET_SMOOTHING: case PLATFORM_ADC_GET_SMOOTHING:
res = adc_state[id].smoothbuffsz; res = adc_state[ id ].smoothlen;
break; break;
case PLATFORM_ADC_SET_SMOOTHING: case PLATFORM_ADC_SET_SMOOTHING:
// If buffer length changes, alloc new buffer, and reset position // If buffer length changes, alloc new buffer, and reset position
if( (u16) data != adc_state[id].smoothbuffsz ) adc_update_smoothing( &adc_state[ id ], ( u8 )data );
{
adc_state[id].smoothbuffsz = (u16) data;
// Free old buffer space
free(adc_state[id].smoothbuff); // FIXME? Compiler complains
// Reset sum, avg, index location
adc_state[id].smoothbuffidx = 0;
adc_state[id].smoothingav = 0;
adc_state[id].smoothingsum = 0;
// Allocate and zero new smoothing buffer
if( ( adc_state[id].smoothbuff = malloc( adc_state[id].smoothbuffsz * sizeof( u16 ) ) ) == NULL )
{
return 1;
}
for( ctr = 0; ctr < adc_state[id].smoothbuffsz; ctr ++ )
adc_state[id].smoothbuff[ctr] = 0;
}
break; break;
} }
return res; return res;
@ -628,8 +678,7 @@ u32 platform_adc_op( unsigned id, int op, u32 data ) // Move to common?
u16 platform_adc_sample( unsigned id ) u16 platform_adc_sample( unsigned id )
{ {
// If no sample is pending or if were configured for burst, set to single-shot // If no sample is pending or if were configured for burst, set to single-shot
if ( HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_PENDING ) == 0 || \ if ( adc_state[ id ].op_pending == 0 || adc_state[ id ].burst == 1)
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_BURST ) == 1)
{ {
// Make sure sequencer is disabled before making changes // Make sure sequencer is disabled before making changes
ADCSequenceDisable( ADC_BASE, id ); ADCSequenceDisable( ADC_BASE, id );
@ -639,30 +688,29 @@ u16 platform_adc_sample( unsigned id )
// Restart Sequencer // Restart Sequencer
ADCSequenceEnable( ADC_BASE, id); ADCSequenceEnable( ADC_BASE, id);
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_BURST ) = 0;
adc_state[ id ].burst = 0;
} }
// Fire Trigger to start sample conversion // Fire Trigger to start sample conversion
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_PENDING ) = 1; adc_state[ id ].op_pending = 1;
ADCProcessorTrigger( ADC_BASE, id ); ADCProcessorTrigger( ADC_BASE, id );
// If in blocking mode and sample is pending, wait for ready flag // If in blocking mode and sample is pending, wait for ready flag
if ( HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_NONBLOCKING ) == 0 && \ if ( adc_state[ id ].nonblocking == 0 && adc_state[ id ].op_pending == 1 )
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_PENDING ) == 1 )
{ {
while ( HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_DATA_READY ) == 0 ) { ; } while ( adc_state[ id ].data_ready == 0 ) { ; }
} }
// If non-blocking we return whatever we have in the buffer // Return last sample and mark used
// Mark sample as old, and return adc_state[ id ].data_ready = 0;
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_DATA_READY ) = 0; return adc_state[ id ].sample;
return adc_state[id].burstbuff[0];
} }
// returns 1 if the conversion on the specified channel ended, 0 otherwise // returns 1 if the conversion on the specified channel ended, 0 otherwise
int platform_adc_is_done( unsigned id ) int platform_adc_is_done( unsigned id )
{ {
return HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_DATA_READY ); return adc_state[ id ].data_ready;
} }
// sets the mode on the specified ADC channel to either blocking (0) or non-blocking (1) // sets the mode on the specified ADC channel to either blocking (0) or non-blocking (1)
@ -671,20 +719,18 @@ void platform_adc_set_mode( unsigned id, int mode )
switch( mode ) switch( mode )
{ {
case PLATFORM_ADC_BLOCKING: case PLATFORM_ADC_BLOCKING:
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_NONBLOCKING ) = 0; adc_state[ id ].nonblocking = 0;
break; break;
case PLATFORM_ADC_NONBLOCKING: case PLATFORM_ADC_NONBLOCKING:
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_NONBLOCKING ) = 1; adc_state[ id ].nonblocking = 1;
break; break;
} }
} }
// burst conversion: read "count" samples from the ADC channel "id", storing the results in "buf". The samples are read at periodic intervals, the period is given by "frequency".
/* -- not ready yet --
void platform_adc_burst( unsigned id, u16* buf, unsigned count, unsigned timer_id, u32 frequency ) void platform_adc_burst( unsigned id, u16* buf, unsigned count, unsigned timer_id, u32 frequency )
{ {
if( HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_BURST ) == 0 ) if( adc_state[ id ].burst == 0 )
{ {
// Make sure sequencer is disabled before making changes // Make sure sequencer is disabled before making changes
ADCSequenceDisable( ADC_BASE, id ); ADCSequenceDisable( ADC_BASE, id );
@ -694,17 +740,36 @@ void platform_adc_burst( unsigned id, u16* buf, unsigned count, unsigned timer_i
// Restart Sequencer // Restart Sequencer
ADCSequenceEnable( ADC_BASE, id ); ADCSequenceEnable( ADC_BASE, id );
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_BURST ) = 1; adc_state[ id ].burst = 1;
} }
HWREGBITW( &adc_state[id].status, PLATFORM_ADC_CH_PENDING ) = 1; // If we have a new buffer, replace the old one
// - same buffer a way to get previously requested non-blocking data?
//if (adc_state[ id ].burstbuf != buf)
//{
adc_state[ id ].burstbuf = buf;
adc_state[ id ].burstlen = count;
adc_state[ id ].burstidx = 0;
//}
// Enable Timer adc_state[ id ].timer_id = timer_id;
adc_state[ id ].op_pending = 1;
// Setup timer and go
TimerConfigure(timer_base[timer_id], TIMER_CFG_32_BIT_PER); TimerConfigure(timer_base[timer_id], TIMER_CFG_32_BIT_PER);
TimerLoadSet(timer_base[timer_id], TIMER_A, SysCtlClockGet() / frequency); TimerLoadSet(timer_base[timer_id], TIMER_A, SysCtlClockGet() / frequency);
TimerControlTrigger(timer_base[timer_id], TIMER_A, true); TimerControlTrigger(timer_base[timer_id], TIMER_A, true);
TimerEnable(timer_base[timer_id], TIMER_A);
// If in blocking mode and sampling task is pending, wait until buffer fills
//if ( adc_state[ id ].nonblocking == 0 && adc_state[ id ].op_pending == 1 )
//{
while ( adc_state[ id ].data_ready == 0 ) { ; }
// Mark data as used
adc_state[ id ].data_ready = 0;
//}
} }
*/
// **************************************************************************** // ****************************************************************************
// OLED Display specific functions // OLED Display specific functions