From d38127a60dd233e9752e2e7745d538d554b4dbb7 Mon Sep 17 00:00:00 2001 From: rs <> Date: Wed, 30 Dec 2020 21:27:30 -0600 Subject: [PATCH] Separate modes and rename pins --- cap_meter.ino | 208 +++++++++++++++++++++++++++----------------------- 1 file changed, 111 insertions(+), 97 deletions(-) diff --git a/cap_meter.ino b/cap_meter.ino index c4eed10..0c367be 100644 --- a/cap_meter.ino +++ b/cap_meter.ino @@ -3,116 +3,129 @@ #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); -- 2.43.0