#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);
}
// Display
+// Using Adafruit's four-digit 7-segment display backpack
Adafruit_7segment segs = Adafruit_7segment();
// Pin assignments for unit indicator LEDs
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)
{
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);