added oversampling with filtering and averaging of oversampled values
authorJim Morris <morris@wolfman.com>
Sun, 26 Jul 2015 22:46:22 +0000 (15:46 -0700)
committerJim Morris <morris@wolfman.com>
Sun, 26 Jul 2015 22:46:22 +0000 (15:46 -0700)
src/libs/ADC/adc.cpp
src/libs/Adc.cpp
src/libs/Adc.h
src/modules/tools/temperaturecontrol/Thermistor.cpp
src/modules/tools/temperaturecontrol/Thermistor.h

index 258e067..945b5d1 100644 (file)
@@ -40,7 +40,7 @@ ADC::ADC(int sample_rate, int cclk_div)
             LPC_SC->PCLKSEL0 |= 0x3 << 24;
             break;
         default:
-            printf("Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n",
+            printf("ADC Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n",
                 cclk_div);
             printf("Defaulting to 1.\n");
             LPC_SC->PCLKSEL0 |= 0x1 << 24;
@@ -50,21 +50,20 @@ ADC::ADC(int sample_rate, int cclk_div)
     clock_div=pclk / adc_clk_freq;
 
     if (clock_div > 0xFF) {
-        //printf("Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n",
-        //    clock_div);
+        printf("ADC Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n", clock_div);
         clock_div=0xFF;
     }
     if (clock_div == 0) {
-        printf("Warning: Clock division is 0. Re-Setting to 1.\n");
+        printf("ADC Warning: Clock division is 0. Re-Setting to 1.\n");
         clock_div=1;
     }
 
     _adc_clk_freq=pclk / clock_div;
     if (_adc_clk_freq > MAX_ADC_CLOCK) {
-        printf("Warning: Actual ADC sample rate of %u which is above %u limit\n",
+        printf("ADC Warning: Actual ADC sample rate of %u which is above %u limit\n",
             _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE);
         while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++;
-        printf("Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
+        printf("ADC Warning: Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
     }
 
     LPC_ADC->ADCR =
@@ -296,7 +295,7 @@ void ADC::select(PinName pin) {
 void ADC::burst(int state) {
     if ((state & 1) == 1) {
         if (startmode(0) != 0)
-            fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0));
+            fprintf(stderr, "ADC Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0));
         LPC_ADC->ADCR |= (1 << 16);
     }
     else
index 8112d51..f97b56e 100644 (file)
@@ -31,7 +31,10 @@ static void sample_isr(int chan, uint32_t value)
 Adc::Adc()
 {
     instance = this;
-    this->adc = new mbed::ADC(1000, 8);
+    // ADC sample rate need to be fast enough to be able to read the enabled channels within the thermistor poll time
+    // even though ther maybe 32 samples we only need one new one within the polling time
+    const uint32_t sample_rate= 1000; // 1KHz sample rate
+    this->adc = new mbed::ADC(sample_rate, 8);
     this->adc->append(sample_isr);
 }
 
@@ -89,6 +92,23 @@ unsigned int Adc::read(Pin *pin)
     // returns the median value of the last 8 samples
     return median_buffer[quick_median(median_buffer, num_samples)];
 
+#elif defined(OVERSAMPLE)
+    // Oversample to get 2 extra bits of resolution
+    // weed out top and bottom worst values then oversample the rest
+    // put into a 4 element moving average and return the average of the last 4 oversampled readings
+    static uint16_t ave_buf[num_channels][4]{0,0,0,0};
+    std::sort(median_buffer, median_buffer + num_samples);
+    uint32_t sum = 0;
+    for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) {
+        sum += median_buffer[i];
+    }
+    // this slows down the rate of change a little bit
+    ave_buf[channel][3]= ave_buf[channel][2];
+    ave_buf[channel][2]= ave_buf[channel][1];
+    ave_buf[channel][1]= ave_buf[channel][0];
+    ave_buf[channel][0]= sum >> OVERSAMPLE;
+    return (ave_buf[channel][0]+ave_buf[channel][1]+ave_buf[channel][2]+ave_buf[channel][3])/4;
+
 #else
     // sort the 8 readings and return the average of the middle 4
     std::sort(median_buffer, median_buffer + num_samples);
@@ -97,6 +117,7 @@ unsigned int Adc::read(Pin *pin)
         sum += median_buffer[i];
     }
     return sum / (num_samples / 2);
+
 #endif
 }
 
index 4b1d7a9..163d0a8 100644 (file)
 
 #include "PinNames.h" // mbed.h lib
 
+#include <cmath>
+
 class Pin;
 namespace mbed {
     class ADC;
 }
 
+// define how many bits of extra resolution required
+// 2 bits means the 12bit ADC is 14 bits of resolution
+#define OVERSAMPLE 2
+
 class Adc
 {
 public:
@@ -26,14 +32,24 @@ public:
 
     static Adc *instance;
     void new_sample(int chan, uint32_t value);
+    // return the maximum ADC value, base is 12bits 4095.
+#ifdef OVERSAMPLE
+    int get_max_value() const { return 4095 << OVERSAMPLE;}
+#else
+    int get_max_value() const { return 4095;}
+#endif
 
 private:
     PinName _pin_to_pinname(Pin *pin);
     mbed::ADC *adc;
 
-
     static const int num_channels= 6;
+#ifdef OVERSAMPLE
+    // we need 4^n sample to oversample and we get double that to filter out spikes
+    static const int num_samples= powf(4, OVERSAMPLE)*2;
+#else
     static const int num_samples= 8;
+#endif
     // buffers storing the last num_samples readings for each channel
     uint16_t sample_buffers[num_channels][num_samples];
 };
index 84add1a..356cb9c 100644 (file)
@@ -217,8 +217,10 @@ void Thermistor::get_raw()
     }
 
     int adc_value= new_thermistor_reading();
+    const uint32_t max_adc_value= THEKERNEL->adc->get_max_value();
+
      // resistance of the thermistor in ohms
-    float r = r2 / ((4095.0F / adc_value) - 1.0F);
+    float r = r2 / (((float)max_adc_value / adc_value) - 1.0F);
     if (r1 > 0.0F) r = (r1 * r) / (r1 - r);
 
     THEKERNEL->streams->printf("adc= %d, resistance= %f\n", adc_value, r);
@@ -234,13 +236,14 @@ void Thermistor::get_raw()
     }
 }
 
-float Thermistor::adc_value_to_temperature(int adc_value)
+float Thermistor::adc_value_to_temperature(uint32_t adc_value)
 {
-    if ((adc_value >= 4095) || (adc_value == 0))
+    const uint32_t max_adc_value= THEKERNEL->adc->get_max_value();
+    if ((adc_value >= max_adc_value) || (adc_value == 0))
         return infinityf();
 
     // resistance of the thermistor in ohms
-    float r = r2 / ((4095.0F / adc_value) - 1.0F);
+    float r = r2 / (((float)max_adc_value / adc_value) - 1.0F);
     if (r1 > 0.0F) r = (r1 * r) / (r1 - r);
 
     if(r > this->r0 * 8) return infinityf(); // 800k is probably open circuit
index e06c290..c92551d 100644 (file)
@@ -33,7 +33,7 @@ class Thermistor : public TempSensor
 
     private:
         int new_thermistor_reading();
-        float adc_value_to_temperature(int adc_value);
+        float adc_value_to_temperature(uint32_t adc_value);
         void calc_jk();
 
         // Thermistor computation settings using beta, not used if using Steinhart-Hart