]> git.the-white-hart.net Git - atmega/cap_meter.git/commitdiff
Separate modes and rename pins
authorrs <>
Thu, 31 Dec 2020 03:27:30 +0000 (21:27 -0600)
committerrs <>
Thu, 31 Dec 2020 03:27:30 +0000 (21:27 -0600)
cap_meter.ino

index c4eed1068f48ef042b067e970deb47ffd567c342..0c367becbcb5499d02f4bca270aa692f6cd72636 100644 (file)
 #include "Adafruit_LEDBackpack.h"
 
 
+const float INF = 1.0 / 0.0;
+
+
 //------------------------------------------------------------------------------
 // Measurement
 
 
 const int ADC_MAX = 1023;
 
-const int PIN_SENSE = A2;
-const int PIN_SOURCE = A0;
+const int PIN_G = A2;         // Use this pin for the - lead of polarized caps
+const int PIN_V = A0;         // Use this pin for the + lead of polarized caps
 
+// Adjust these values as needed to calibrate
 const float Ccal = 1.75e-12;  // Measured cap when no test cap is present
 const float Cin = 24.48e-12;  // Input capacitance of the ADC
 const float Rpull = 34.8e3;   // Digital input pull-up resistance
 
 
-void measurement_init(void)
+void measurement_discharge(void)
 {
-  pinMode(PIN_SOURCE, OUTPUT);
-  pinMode(PIN_SENSE, OUTPUT);
-  digitalWrite(PIN_SOURCE, LOW);
-  digitalWrite(PIN_SENSE, LOW);
+  // Make sure the capacitor is discharged
+  pinMode(PIN_G, OUTPUT);
+  pinMode(PIN_V, OUTPUT);
+  digitalWrite(PIN_G, LOW);
+  digitalWrite(PIN_V, LOW);
+  delay(10);
 }
 
 
-float measurement_read(void)
+// Small capacitors (< ~100pF) can be measured by forming a voltage divider
+// with the input capacitance of the sense pin.  Use the voltage divider formula
+// to infer the capacitance.
+//
+//       Cin     (V)        (Vs)
+// GND----||-------          -------------Vcc
+//                |          |                internal
+// ...............|..........|........................
+//           PIN_G|          |PIN_V           external
+//                |          |
+//                -----||-----
+//                     C
+//                    (Vc)
+//
+// Vc * C = V * Cin  and  Vc = Vs - V
+//
+//     V * Cin
+// C = -------
+//     Vs - V
+float measurement_small(void)
 {
-  // Make extra sure the capacitor is discharged
-  pinMode(PIN_SENSE, OUTPUT);
-  pinMode(PIN_SOURCE, OUTPUT);
-  digitalWrite(PIN_SENSE, LOW);
-  digitalWrite(PIN_SOURCE, LOW);
-  delay(1);
-
-  pinMode(PIN_SENSE, INPUT);
-  digitalWrite(PIN_SOURCE, HIGH);
-  int val = analogRead(PIN_SENSE);
-  digitalWrite(PIN_SOURCE, LOW);
-
-  if (val < 1000)
-  {
-    // The capacitor to measure was small enough that it gave a low enough
-    // voltage to be resolvable when forming a voltage divider with the input
-    // capacitance of the sense pin.  Use the voltage divider formula to infer
-    // the capacitance.
-    //
-    //       Cin     (V)        (Vs)
-    // GND----||-------          -------------Vcc
-    //                |          |
-    // ...............|..........|...............
-    //       PIN_SENSE|          |PIN_SOURCE
-    //                |          |
-    //                -----||-----
-    //                     C
-    //                    (Vc)
-    //
-    // Vc * C = V * Cin  and  Vc = Vs - V
-    //
-    //     V * Cin
-    // C = -------
-    //     Vs - V
-
-    return (float)val * Cin / (float)(ADC_MAX - val) - Ccal;
-  }
-  else
-  {
-    // The capacitor to measure dominated the voltage divider too much to give a
-    // useful measurement, so measure its time constant against the internal
-    // input pullup resistor value.
-    //
-    //                               Rpull
-    // GND-------------          -----^^^-----Vcc
-    //                |          |
-    // ...............|..........|...............
-    //       PIN_SENSE|          |PIN_SOURCE
-    //                |          |
-    //                -----||-----
-    //                     C
-    //                    (Vc)
-    //
-
-    unsigned long t_start;
-    unsigned long t_end;
-    unsigned long t_charge;
-    int dig_val;
-
-    // Ground one pin and connect the pullup to Vcc on the other
-    pinMode(PIN_SENSE, OUTPUT);
-    delay(1);
-    pinMode(PIN_SOURCE, INPUT_PULLUP);
-
-    // Wait for the pulled-up input to read 1, when the capacitor is charged
-    t_start = micros();
-    while (!digitalRead(PIN_SOURCE));
-    t_end = micros();
-    t_charge = t_end > t_start ? t_end - t_start : t_start - t_end;
-
-    // Read the voltage the capacitor was charged up to
-    pinMode(PIN_SOURCE, INPUT);
-    val = analogRead(PIN_SOURCE);
-
-    // Discharge the capacitor
-    digitalWrite(PIN_SENSE, HIGH);
-    delay(t_charge / 1000 * 5);
-
-    // Reset the pin states
-    pinMode(PIN_SOURCE, OUTPUT);
-    digitalWrite(PIN_SOURCE, LOW);
-    digitalWrite(PIN_SENSE, LOW);
-
-    return -(float)t_charge * 1e-6 / Rpull / log(1.0 - (float)val / (float)ADC_MAX);
-  }
+  int analog_val;
+
+  // Make sure the capacitor is discharged so we start fresh.
+  measurement_discharge();
+
+  // Make a voltage divider with the input capacitance
+  pinMode(PIN_G, INPUT);
+  pinMode(PIN_V, OUTPUT);
+  digitalWrite(PIN_V, HIGH);
+
+  // Measure the voltage at the center of the voltage divider
+  analog_val = analogRead(PIN_G);
+
+  // Clean up after ourselves
+  measurement_discharge();
+
+  // The capacitor is too large to get a good measurement.
+  if (analog_val >= 1000)
+    return INF;
+
+  // Use the capacitor voltage divider formula to infer the capacitance.
+  // Also subtract off the extra "calibrated" capacitance.
+  return (float)analog_val * Cin / (float)(ADC_MAX - analog_val) - Ccal;
+}
+
+
+// Larger capacitors (> ~100pF) can be measured by the time-to-charge method
+// because the parasitic capacitance will be insignificant.
+//
+//                               Rpull
+// GND-------------          -----^^^-----Vcc
+//                |          |                internal
+// ...............|..........|........................
+//           PIN_G|          |PIN_V           external
+//                |          |
+//                -----||-----
+//                     C
+//                    (Vc)
+//
+float measurement_large(void)
+{
+  unsigned long t_start;
+  unsigned long t_end;
+  unsigned long t_charge;
+  int analog_val;
+  int dig_val;
+
+  // Make sure the capacitor is discharged so we start fresh.
+  measurement_discharge();
+
+  // Ground one pin and connect the pullup to Vcc on the other.
+  pinMode(PIN_G, OUTPUT);
+  digitalWrite(PIN_G, LOW);
+  pinMode(PIN_V, INPUT_PULLUP);
+
+  // Wait for the pulled-up input to read 1 when the capacitor is charged.
+  t_start = micros();
+  while (!digitalRead(PIN_V));
+  t_end = micros();
+
+  // Stop charging and read the voltage the cap was charged to.
+  pinMode(PIN_V, INPUT);
+  analog_val = analogRead(PIN_V);
+
+  // Clean up after ourselves.
+  measurement_discharge();
+
+  // Charge time is the difference between start and end times.
+  // The conditional is to deal with the micros() timer overflowing.
+  t_charge = t_end > t_start ? t_end - t_start : t_start - t_end;
+
+  // Compute the capacitance from the V(t) for and RC circuit.
+  return -(float)t_charge * 1e-6 / Rpull / log(1.0 - (float)analog_val / (float)ADC_MAX);
 }
 
 
@@ -120,6 +133,7 @@ float measurement_read(void)
 // Display
 
 
+// Using Adafruit's four-digit 7-segment display backpack
 Adafruit_7segment segs = Adafruit_7segment();
 
 // Pin assignments for unit indicator LEDs
@@ -135,8 +149,6 @@ const float P_N_BOUND = 1.0e-9;
 const float N_U_BOUND = 1.0e-6;
 const float U_H_BOUND = 1.0e-3;
 
-const float INF = 1.0 / 0.0;
-
 
 void display_init(void)
 {
@@ -241,14 +253,16 @@ void display_show_serial(float value)
 void setup() {
   Serial.begin(9600);
   display_init();
-  measurement_init();
+  measurement_discharge();
 }
 
 
 void loop() {
   float capacitor_value;
 
-  capacitor_value = measurement_read();
+  capacitor_value = measurement_small();
+  if (capacitor_value == INF)
+    capacitor_value = measurement_large();
 
   display_show(capacitor_value);
   display_show_serial(capacitor_value);