Dependencies:   mbed

main.cpp

Committer:
bobanderson
Date:
2011-02-18
Revision:
2:e1781d02ec0d
Parent:
1:ecca38babc13

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;
}