Following on from this thread I decided to try to make a capacitance tester with my Arduino Uno.
It works pretty well for fairly low-value capacitors.
Circuit:
The objective here is to find the time interval T (tau really) in the equation:
T = R * C
Solved for C:
C = T / R
So we want to charge the capacitor under test with a suitable voltage (eg. 5V from an Arduino output pin) and measure how long it takes to reach 63.2% of that voltage. So we need a reference voltage of 5 * 0.632, namely 3.16V.
The voltage divider illustrated above should provide approximately that:
Vout = Vin * (R2 / (R1 + R2))
5 * (3100 / (1800 + 3100)) = 3.1632653061224
You may need to use a few resistors (eg. 3 x 1K plus 100 ohms) to make up the 3100 ohm resistor.
We now need to time the interval indicated by the arrow and stop when the rising edge reaches the reference voltage:
The internal analogue comparator is just the thing for the job. We connect the reference voltage to the AIN1 pin (negative reference) and connect our capacitor to the AIN0 pin (positive reference) and then configure an interrupt on the rising edge. I chose a value of 10K for the resistor to give a reasonably slow charge time.
A simple sketch follows:
/*
Capacitance meter
Author: Nick Gammon
Date: 27 June 2013
Pulse pin (D2): Connect to capacitor via 10K resistor.
Reference voltage of 0.632 of output pin (pulsePin) connected to D7.
In my case I used 3.06V because I measured 4.84 on the 5V pin.
Measure pin (D6) connected to first leg of capacitor, other leg connected to Gnd.
Like this:
Capacitor to test:
D2 ----> 10K ----> D6 ----> capacitor_under_test ----> Gnd
Reference voltage:
+5V ----> 1.8K ---> D7 ---> 3.1K ----> Gnd
*/
const byte pulsePin = 2;
const unsigned long resistance = 10000;
volatile boolean triggered;
volatile boolean active;
volatile unsigned long startTime;
volatile unsigned long duration;
ISR (ANALOG_COMP_vect)
{
// grab time quickly
unsigned long now = micros ();
if (active)
{
duration = now - startTime;
triggered = true;
digitalWrite (pulsePin, LOW); // start discharging capacitor
}
}
void setup ()
{
pinMode (pulsePin, OUTPUT);
digitalWrite (pulsePin, LOW);
Serial.begin (115200);
Serial.println ("Started.");
ADCSRB = 0; // (Disable) ACME: Analog Comparator Multiplexer Enable
ACSR = _BV (ACI) // (Clear) Analog Comparator Interrupt Flag
| _BV (ACIE) // Analog Comparator Interrupt Enable
| _BV (ACIS0) | _BV (ACIS1); // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on rising edge)
} // end of setup
void loop ()
{
// start another test?
if (!active)
{
active = true;
triggered = false;
digitalWrite (pulsePin, HIGH); // start charging capacitor
startTime = micros (); // remember when
}
// if the ISR noted the time interval is up, display results
if (active && triggered)
{
active = false;
Serial.print ("Capacitance = ");
Serial.print (duration * 1000 / resistance);
Serial.println (" nF");
triggered = false;
delay (3000);
}
} // end of loop
With a 47 nF capacitor connected, I get this result:
Started.
Capacitance = 47 nF
Capacitance = 46 nF
Capacitance = 47 nF
Capacitance = 46 nF
Not too bad. But with a 6.8 nF capacitor we only get one decimal place:
Capacitance = 6 nF
Capacitance = 7 nF
Capacitance = 7 nF
Capacitance = 7 nF
Capacitance = 6 nF
Capacitance = 6 nF
Capacitance = 7 nF
Capacitance = 6 nF
Capacitance = 7 nF
Plus, the micros() function only returns time to the nearest 4 microseconds.
A slightly more elaborate sketch uses Timer 1 with a prescaler of 1 to get a higher resolution timer.
/*
Capacitance meter
Author: Nick Gammon
Date: 27 June 2013
Pulse pin (D2): Connect to capacitor via 10K resistor.
Reference voltage of 0.632 of output pin (pulsePin) connected to D7.
In my case I used 3.06V because I measured 4.84 on the 5V pin.
Measure pin (D6) connected to first leg of capacitor, other leg connected to Gnd.
Like this:
Capacitor to test:
D2 ----> 10K ----> D6 ----> capacitor_under_test ----> Gnd
Reference voltage:
+5V ----> 1.8K ---> D7 ---> 3.1K ----> Gnd
*/
const byte pulsePin = 2;
const float resistance = 10000.0;
volatile boolean triggered;
volatile boolean active;
volatile unsigned long timerCounts;
volatile unsigned long overflowCount;
ISR (TIMER1_OVF_vect)
{
++overflowCount; // count number of Counter1 overflows
} // end of TIMER1_OVF_vect
ISR (ANALOG_COMP_vect)
{
// grab counter value before it changes any more
unsigned int timer1CounterValue;
timer1CounterValue = TCNT1; // see datasheet, page 117 (accessing 16-bit registers)
if (active)
{
// if just missed an overflow
if (TIFR1 & TOV1)
overflowCount++;
// calculate total count
timerCounts = (overflowCount << 16) + timer1CounterValue; // each overflow is 65536 more
triggered = true;
digitalWrite (pulsePin, LOW); // start discharging capacitor
}
}
void setup ()
{
pinMode (pulsePin, OUTPUT);
digitalWrite (pulsePin, LOW);
Serial.begin (115200);
Serial.println ("Started.");
ADCSRB = 0; // (Disable) ACME: Analog Comparator Multiplexer Enable
ACSR = _BV (ACI) // (Clear) Analog Comparator Interrupt Flag
| _BV (ACIE) // Analog Comparator Interrupt Enable
| _BV (ACIS0) | _BV (ACIS1); // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on rising edge)
} // end of setup
void startTiming ()
{
active = true;
triggered = false;
// prepare timer
overflowCount = 0; // no overflows yet
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0; // Counter to zero
// Timer 1 - counts clock pulses
TIMSK1 = _BV (TOIE1); // interrupt on Timer 1 overflow
// get on with it
digitalWrite (pulsePin, HIGH); // start charging capacitor
// start Timer 1, no prescaler
TCCR1B = _BV (CS10);
} // end of startTiming
void finishTiming ()
{
active = false;
Serial.print ("Capacitance = ");
float capacitance = (float) timerCounts * 1000.0 / 16.0 / resistance;
Serial.print (capacitance);
Serial.println (" nF");
triggered = false;
delay (3000);
} // end of finishTiming
void loop ()
{
// start another test?
if (!active)
startTiming ();
// if the ISR noted the time interval is up, display results
if (active && triggered)
finishTiming ();
} // end of loop
Output for 6.8 nF capacitor:
Capacitance = 6.96 nF
Capacitance = 6.96 nF
Capacitance = 6.96 nF
Capacitance = 6.95 nF
Capacitance = 6.96 nF
Capacitance = 6.97 nF
And the 47 nF capacitor looks good:
Capacitance = 46.94 nF
Capacitance = 46.91 nF
Capacitance = 46.94 nF
Capacitance = 46.85 nF
Capacitance = 46.91 nF
Capacitance = 46.91 nF
Capacitance = 46.91 nF
Unfortunately a 100 nF capacitor seems to give a high reading:
Started.
Capacitance = 126.75 nF
Capacitance = 126.67 nF
Capacitance = 126.61 nF
Capacitance = 126.74 nF
Capacitance = 126.65 nF
Capacitance = 126.69 nF
Does anyone know why this would be? Is there leakage in the capacitor which makes it take longer to charge, or something? Judging by research I have done measuring capacitance is not really trivial, so I suppose I should be happy it works reasonably well.