AnalogIn - use median of the sampling data for stabler input value

Dependencies:   mbed

Revision:
0:bbe67df5e586
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AnalogIn2.cpp	Mon Jun 20 10:47:02 2011 +0000
@@ -0,0 +1,166 @@
+#include "AnalogIn2.h"
+
+short int quickSelect(unsigned short arr[], int n);
+
+AnalogIn2::AnalogIn2(PinName pinName) : pinName(pinName) {
+    switch (pinName) {
+        case p15:
+            channel = 0;
+            break;
+        case p16:
+            channel = 1;
+            break;
+        case p17:
+            channel = 2;
+            break;
+        case p18:
+            channel = 3;
+            break;
+        case p19:
+            channel = 4;
+            break;
+        case p20:
+            channel = 5;
+            break;
+    }
+    read_u16(1);
+}
+
+unsigned short AnalogIn2::read_u16(int nSamples) {
+    // power on, clk divider /4
+    LPC_SC->PCONP |= (1 << 12);
+    LPC_SC->PCLKSEL0 &= ~(0x3 << 24);
+
+    // software-controlled ADC settings
+    LPC_ADC->ADCR = (0 << 0) // SEL: 0 = no channels selected
+                    | (25 << 8)    // CLKDIV: PCLK max ~= 25MHz, /25 to give safe 1MHz
+                    | (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
+
+    switch (pinName) {
+        case p15:// =p0.23 of LPC1768
+            LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+            LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14;
+            LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+            LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14;
+            break;
+        case p16:// =p0.24 of LPC1768
+            LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+            LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16;
+            LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+            LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16;
+            break;
+        case p17:// =p0.25 of LPC1768
+            LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+            LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18;
+            LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+            LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18;
+            break;
+        case p18:// =p0.26 of LPC1768:
+            LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+            LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20;
+            LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+            LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20;
+            break;
+        case p19:// =p1.30 of LPC1768
+            LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+            LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28;
+            LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+            LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28;
+            break;
+        case p20:// =p1.31 of LPC1768
+            LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+            LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30;
+            LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+            LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30;
+            break;
+    }
+
+    // Repeatedly get the sample data until DONE bit
+    unsigned short a[nSamples];
+    for (int i = 0; i < nSamples; i++) {
+        unsigned int data;
+        // Select channel and start conversion
+        LPC_ADC->ADCR &= ~0xFF;
+        LPC_ADC->ADCR |= 1 << channel;
+        LPC_ADC->ADCR |= 1 << 24;
+        do {
+            data = LPC_ADC->ADGDR;
+        } while ((data & ((unsigned int)1 << 31)) == 0);
+        // Stop conversion
+        LPC_ADC->ADCR &= ~(1 << 24);
+
+        a[i] = data & 65535;
+    }
+
+    return quickSelect(a, nSamples);
+}
+
+float AnalogIn2::read(int nSamples) {
+    return (read_u16(nSamples) >> 4) / 4096.0;
+}
+
+AnalogIn2::operator float() {
+    return read();
+}
+
+/*
+ * This Quickselect routine is based on the algorithm described in
+ * "Numerical recipes in C", Second Edition,
+ * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
+ * This code by Nicolas Devillard - 1998. Public domain. */
+
+#define SWAP(a,b) {unsigned int t = (a); (a) = (b); (b) = t;}
+
+short int quickSelect(unsigned short arr[], int n) {
+    int low = 0, high = n - 1;
+    unsigned int median = (low + high) / 2;
+
+    for (;;) {
+        if (high <= low) /* One element only */
+            return arr[median];
+        if (high == low + 1) { /* Two elements only */
+            if (arr[low] > arr[high])
+                SWAP(arr[low], arr[high]);
+            return arr[median];
+        }
+
+        /* Find median of low, middle and high items; swap into position low */
+        int middle = (low + high) / 2;
+        if (arr[middle] > arr[high])
+            SWAP(arr[middle], arr[high]);
+        if (arr[low] > arr[high])
+            SWAP(arr[low], arr[high]);
+        if (arr[middle] > arr[low])
+            SWAP(arr[middle], arr[low]);
+        /* Swap low item (now in position middle) into position (low + 1) */
+        SWAP(arr[middle], arr[low + 1]);
+
+        /* Nibble from each end towards middle, swapping items when stuck */
+        int ll = low + 1;
+        int hh = high;
+        for (;;) {
+            do {
+                ll++;
+            } while (arr[low] > arr[ll]);
+            do {
+                hh--;
+            } while (arr[hh] > arr[low]);
+            if (hh < ll)
+                break;
+            SWAP(arr[ll], arr[hh]);
+        }
+
+        /* Swap middle item (in position low) back into correct position */
+        SWAP(arr[low], arr[hh]);
+
+        /* Re-set active partition */
+        if (hh <= median)
+            low = ll;
+        if (hh >= median)
+            high = hh - 1;
+    }
+}
\ No newline at end of file