It looks like Ignacio Vazquez-Abrams and I have been doing similar things. :)
I have a page about making a capacitor tester:
Turn your Arduino into a capacitor tester
However since link-only replies are frowned on (the link might go down) I'll summarize it here.
The simple approach is to charge the capacitor until it reaches 63.2% of the charging voltage.
That is because:
1 - e^(-1) = 0.63212
See RC Time constant - Wikipedia.
We can use the Analog Comparator to trigger an interrupt when the voltage reaches that level.
We need the capacitor to be on pin D6 on the Uno and a reference voltage on pin D7.
See Using the Arduino Analog Comparator for more details about the Analog Comparator.
This is much faster than using analogRead, because it triggers on an exact voltage.
Your basic objective is to find how long it takes to reach 63.2% of the supplied voltage (in the example being 1 volt):

In this particular example it took 47 µs for a 47 nF capacitor:

Instead of using an exact reference voltage (ie. 63.2% of 5 volts) you can do some fancy maths instead. Here is my test circuit:

DUT = Device Under Test
We plug the values of the resistors into the code like this:
const float Rc = 10000; // charging resistor
const float R1 = 1000; // between ground and D7
const float R2 = 1800; // between +5V and D7
const float clockRate_us = 16; // 16 MHz
const float k = 1000 / (clockRate_us * Rc * log ((R1 + R2) / R2));
Note: For high accuracy you should use the measured resistance values, not just the nominal ones.
Now with some fairly simple code we can use the constant k to work out the capacitance:
/*
Capacitance meter
Author: Nick Gammon
Date: 2 July 2013
Pulse pin (D2): Connect to capacitor via 10K resistor (Rc)
Reference voltage connected to D7 (AIN1) as per below.
Measure pin (D6 - AIN0) connected to first leg of capacitor, other leg connected to Gnd.
Like this:
Capacitor to test:
D2 ----> Rc ----> D6 ----> capacitor_under_test ----> Gnd
Reference voltage:
+5V ----> R2 ---> D7 ---> R1 ----> Gnd
*/
const byte pulsePin = 2; // the pin used to pulse the capacitor
const float Rc = 10000; // charging resistor
const float R1 = 1000; // between ground and D7
const float R2 = 1800; // between +5V and D7
const float clockRate_us = 16; // 16 MHz
const float k = 1000 / (clockRate_us * Rc * log ((R1 + R2) / R2));
volatile boolean triggered;
volatile boolean active;
volatile unsigned long timerCounts;
volatile unsigned long overflowCount;
ISR (TIMER1_OVF_vect)
{
++overflowCount; // count number of Counter 1 overflows
} // end of TIMER1_OVF_vect
ISR (TIMER1_CAPT_vect)
{
// grab counter value before it changes any more
unsigned int timer1CounterValue;
timer1CounterValue = ICR1; // see datasheet, page 117 (accessing 16-bit registers)
unsigned long overflowCopy = overflowCount;
if (active)
{
// if just missed an overflow
if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
overflowCopy++;
// calculate total count
timerCounts = (overflowCopy << 16) + timer1CounterValue; // each overflow is 65536 more
triggered = true;
digitalWrite (pulsePin, LOW); // start discharging capacitor
TCCR1B = 0; // stop the timer
} // end if active
} // end of TIMER1_CAPT_vect
void setup ()
{
pinMode (pulsePin, OUTPUT);
digitalWrite (pulsePin, LOW);
Serial.begin (115200);
Serial.println ("Started.");
ADCSRB = 0; // (Disable) ACME: Analog Comparator Multiplexer Enable
ACSR = bit (ACIC); // Analog Comparator Input Capture Enable
DIDR1 |= bit (AIN1D) | bit (AIN0D); // Disable digital buffer on comparator inputs
PRR = 0;
} // end of setup
void startTiming ()
{
active = true;
triggered = false;
noInterrupts ();
// 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 = bit (TOIE1) | bit (ICIE1); // interrupt on Timer 1 overflow and input capture
// get on with it
digitalWrite (pulsePin, HIGH); // start charging capacitor
// start Timer 1, no prescaler
TCCR1B = bit (CS10) | bit (ICES1); // plus Input Capture Edge Select
interrupts ();
} // end of startTiming
void finishTiming ()
{
active = false;
Serial.print ("Capacitance = ");
float capacitance = (float) timerCounts * k;
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
For extra accuracy the code uses the Input Capture Unit, which is something that remembers the exact value in Timer 1 when the match occurs. This avoids the delay of 2 to 3 µs while the ISR kicks into action.
Results
I measured a few values using a capacitance-substitution box. To check on the box I measured also with a high-precision multimeter. The value on the right is what the sketch gave (rounded).
(All values in nF)
Nominal Meter Sketch
Value Measure Measure
40 42 42
100 98 102
200 202 201
300 312 313
400 389 391
1000 1004 1010
2000 2011 2012
3000 2950 2953
4000 4160 4170
The sketch output certainly seems to be close to the nominal and measured values.
What about using the internal voltage reference (1.1v) instead of AIN0? which one will be better as the 2xAA batteries voltage drops over time?
The voltage doesn't matter as it is not in the equation. It is the ratio that matters, and that will scale with battery drop (ie. the ratio between the two resistors will always be the same).
You need to use Vcc because it is turned on and off at an output pin (to start charging the capacitor).
Also what is the difference between TIMER1_CAPT_vect which you used here and ANALOG_COMP_vect which you used in the example in your site?
If you are not using the Input Capture Unit, then you need to get the Analog Comparator interrupt. However if you use the Input Capture Unit then you use the Timer1 Capture Event which is triggered when you get the capture.
the intention is to run the system on a battery with clock of 1MHz (to reduce power consumption)
- however it takes longer to do whatever-it-is you are doing. I have a page about minimizing power usage which you may find helpful. – Nick Gammon♦ Sep 1 '15 at 21:48