File content as of revision 2:e1781d02ec0d:
/* Bob Anderson (bob.anderson.ok@gmail.com)
This program...
1) Uses AnalogIn but speeds it up by setting
the ADC clock to 12MHz (13MHz is max from spec sheet)
2) Produces a histogram of deviations around a center
value that is determined at the beginning of a run
by a "vote" taken from three successive readings.
3) Introduces a "glitch" suppression method that appears
to be effective. I consider a glitch to have occurred
when two consecutive reading differ in value by more than
3 sigma (approximately 10 for the noise distributions
exhibited by the internal ADC).
4) Provides for side-by-side comparison of an external ADC
(MCP3304 or MCP3208) running at about 20,000 sps (because
I'm running the part at 3.3v).
With a 100nf capacitor from the analog pin to ground and
a reasonably low impedance source ( < 5K ) and glitch suppression
enabled, the LPC1768/9 onboard analog to digital converter subsystem
becomes quite useable. Without the capacitor and glitch suppression,
it is not a reliable ADC --- it is glitch prone, even with the
cleanest of inputs. This program will help you determine for
yourself whether that statement is true. Play with it.
The comparison of the internal ADC with the external ADC show that the
external ADC is well behaved in circumstances that wipe out the internal ADC
which is apparently quite sensitive to noise, whereas the external ADC is quite
tolerant of noise. The external ADC produces very narrow "distributions".
*/
#include "mbed.h"
Serial pc( USBTX, USBRX );
AnalogIn ain( p20 );
DigitalOut extAdcSelect(p8);
DigitalOut led4(LED4);
SPI spi(p5,p6,p7); // spi(mosi,miso,sck)
#define MCP3208 // or MCP3304
#define EXT_ADC_CHAN 5
#define NUM_SAMPLES 1000000
#define HGRAM_MAX_INDEX 60
int histoGramA[ HGRAM_MAX_INDEX + 1 ]; // histogram array for internal ADC
int histoGramB[ HGRAM_MAX_INDEX + 1 ]; // histogram array for (possible) external ADC
int glitchCount = 0;
bool glitchSuppressionWanted = false;
bool useExternalADC = false;
void setADCclockToMaxConversionRate( void );
int glitchSuppressedAnalogIn( bool );
int readExternalADC( int );
void displayMenu( void );
int getCenterValue( bool externalADC );
int max( int a, int b ) {
if ( a > b ) return a;
return b;
}
int min( int a, int b ) {
if ( a < b ) return a;
return b;
}
// middle( a, b, c ) returns true if a is between b and c
bool middle( int a, int b, int c ) {
return (a <= max(b,c)) && (a >= min(b,c));
}
// The MPC3304 chip select line is inverted: low == selected
#define EXT_ADC_OFF 1
#define EXT_ADC_ON 0
int main() {
int referenceValue;
int delta;
int newValue;
led4 = 0; // This led shows progress during data acquisition.
extAdcSelect = EXT_ADC_OFF; // deselect
spi.format( 8, 0 );
spi.frequency( 500000 );
// Speedup the AnalogIn conversion rate by readjusting
// the ADC clock to 12 MHz (assuming SystemCoreClock = 96MHz)
setADCclockToMaxConversionRate();
while(1) {
displayMenu();
bool waitingForKeyInput = true;
while ( waitingForKeyInput ){
switch (pc.getc()) {
case 'g':
case 'G':
glitchSuppressionWanted = ! glitchSuppressionWanted;
if ( glitchSuppressionWanted )
printf( "...glitch suppression on...\r\n" );
else
printf( "...glitch suppression off...\r\n" );
break;
case 'x':
case 'X':
useExternalADC = ! useExternalADC;
if ( useExternalADC )
printf( "...external ADC will be used...\r\n\r\n" );
else
printf( "...only internal ADC will be used...\r\n" );
break;
case 't':
for ( int cnt = 0; cnt < 50; cnt++ )
printf( "EXT ADC = %d\r\n", readExternalADC(EXT_ADC_CHAN) );
break;
case 's':
case 'S': waitingForKeyInput = false; break;
default:
displayMenu(); break;
}
}
// Clear the histogram arrays.
for ( int k = 0; k <= HGRAM_MAX_INDEX; k++ ) histoGramA[k] = histoGramB[k] = 0;
bool readFromExternalADC = false;
referenceValue = getCenterValue( readFromExternalADC );
printf( "Internal ADC center value = %d\r\n\r\n", referenceValue );
printf( "...now gathering... LED4 toggles at each 10,000 readings\r\n\r\n" );
glitchCount = 0;
for ( int i = 0; i < NUM_SAMPLES; i++ ) {
newValue = glitchSuppressedAnalogIn( readFromExternalADC );
delta = newValue - referenceValue + (HGRAM_MAX_INDEX / 2);
if ( delta < 0 ) histoGramA[0]++;
else if ( delta > HGRAM_MAX_INDEX ) histoGramA[HGRAM_MAX_INDEX]++;
else histoGramA[delta]++;
if ( (i % 10000) == 0 ) led4 = !led4;
}
led4 = 0;
int glitchCountInternal = glitchCount;
int glitchCountExternal = 0;
if ( useExternalADC )
{
readFromExternalADC = true;
referenceValue = getCenterValue( readFromExternalADC );
printf( "External ADC center value = %d\r\n\r\n", referenceValue );
printf( "...now gathering... LED4 toggles at each 10,000 readings\r\n\r\n" );
glitchCount = 0;
for ( int i = 0; i < NUM_SAMPLES; i++ ) {
newValue = glitchSuppressedAnalogIn( readFromExternalADC );
delta = newValue - referenceValue + (HGRAM_MAX_INDEX / 2);
if ( delta < 0 ) histoGramB[0]++;
else if ( delta > HGRAM_MAX_INDEX ) histoGramB[HGRAM_MAX_INDEX]++;
else histoGramB[delta]++;
if ( (i % 10000) == 0 ) led4 = !led4;
}
led4 = 0;
glitchCountExternal = glitchCount;
}
// Output histoGram(s)
for ( int j = 0; j <= HGRAM_MAX_INDEX; j++ ) {
if ( useExternalADC )
printf( "%4d %8d %8d\r\n", j - (HGRAM_MAX_INDEX/2), histoGramA[j], histoGramB[j] );
else
printf( "%4d %8d\r\n", j - (HGRAM_MAX_INDEX/2), histoGramA[j] );
}
// Show glitch stats if glitch suppression was enabled
if ( glitchSuppressionWanted ) {
if ( useExternalADC )
printf( "\nglitchCount: Internal = %d External = %d\r\n",
glitchCountInternal, glitchCountExternal );
else
printf( "\nglitchCountInternal = %d\r\n", glitchCountInternal );
}
}
}
void setADCclockToMaxConversionRate(void) {
// Set pclk = cclk / 4 (pclk = 96Mhz/4 = 24Mhz)
LPC_SC->PCLKSEL0 &= ~(0x3 << 24); // Clear bits 25:24
// software-controlled ADC settings
LPC_ADC->ADCR = (0 << 0) // SEL: 0 = no channels selected
| (1 << 8 ) // CLKDIV: ADCCLK = PCLK / (CLKDIV + 1) (12MHz)
| (0 << 16) // BURST: 0 = software control
| (0 << 17) // CLKS: not applicable
| (1 << 21) // PDN: 1 = operational
| (0 << 24) // START: 0 = no start
| (0 << 27); // EDGE: not applicable}
return;
}
int glitchSuppressedAnalogIn( bool externalADC ) {
int v1,v2,delta;
if ( externalADC )
v1 = readExternalADC(EXT_ADC_CHAN);
else
v1 = ain.read_u16() >> 4;
if ( ! glitchSuppressionWanted ) return v1;
// While this has the possiblity of never returning,
// the probability of that is vanishingly small. We assume
// that we will eventually find two successive readings that are
// within the tolerance band --- i.e., no glitch
while(1){
if ( externalADC )
v2 = readExternalADC(EXT_ADC_CHAN);
else
v2 = ain.read_u16() >> 4;
delta = v1 - v2;
if ( (delta > -10) && (delta < 10)) return (v1+v2)/2;
v1 = v2;
glitchCount++;
}
}
int readExternalADC( int channel ) {
int ch = channel & 0x7;
extAdcSelect = EXT_ADC_ON;
#ifdef MCP3208
int byte1 = spi.write( 0x6 | (ch >> 2) );
int byte2 = spi.write( (ch << 6) ) & 0xf; // d0 = 0
#else
#ifdef MCP3304
int byte1 = spi.write( 0xc | (ch >> 1) );
int byte2 = spi.write( (ch << 7) ) & 0xf; // d0 = 0
#else
error( "Undefined external ADC device" );
#endif
#endif
int byte3 = spi.write( 0xaa ); // dummy value, but nice pattern of logic analyzer
extAdcSelect = EXT_ADC_OFF;
int value = (byte2 << 8) | byte3;
//printf( "byte2 = %2x byte3 = %2x value = %d\r\n", byte2, byte3, value );
return value;
}
void displayMenu( void ) {
if ( glitchSuppressionWanted )
printf( "\r\n\r\nglitch suppression is enabled.\r\n" );
else
printf( "\r\nglitch suppression is disabled\r\n" );
if ( useExternalADC )
printf( "Results from an external ADC will be included.\r\n" );
else
printf( "External ADC not in use.\r\n" );
printf( "\r\nPress the s key to start data acquisition.\r\n" );
printf( "Press the g key to toggle glitch suppression.\r\n" );
printf( "Press the x key to toggle use of external ADC.\r\n\r\n" );
}
int getCenterValue( bool externalADC ) {
// Establish center point for histogram by taking three readings
// and selecting the one in the middle as the reference value around
// which deviations will be calculated.
int value1 = glitchSuppressedAnalogIn( externalADC );
int value2 = glitchSuppressedAnalogIn( externalADC );
int value3 = glitchSuppressedAnalogIn( externalADC );
if ( middle(value1,value2,value3) )
return value1;
else if ( middle(value2,value1,value3) )
return value2;
else
return value3;
}