Capacitance meter (Part 4)

Part 1234

While the principle of operation is correct, the previously described assembly has limitations. It cannot handle the full range of expected capacitance values. Not enough data points are acquired for small capacitors (pF range) and it would take ages before getting the value of large capacitors (F range). This issue can be easily bypassed by prescaling the capacitance measurement. Instead of one single (precision) resistor, the assembly includes now three, each of them is selectable by Arduino.

Note: Do not mind the LM358. Although this amp op will work, best measurements will be obtained using low noise amplifiers. The real reason why I did use this generic chip is that I lost my custom Eagle library. Because it is on c:program files, I did not back up the file that I lost when my HDD crashed

A simple routine is run in order to determine which biaising resistor is the most appropriate for the ultimate reading. No signal burst modelling is calculated, just the time needed to discharge the capacitor down to 13.35% of its initial charge, which corresponds to two time constants. No particular care is taken for the accuracy of this measurement which is subject to variations due to various electrical noises. However, many capacitance meters rely on this straighforward method.

/* Prescaler */
	uint8_t pinCounter = 0;
	double estimatedTimeConstant = 0.0;
	do {
		/* Reset output ports */
		for (uint8_t i = 0; i < 3; i++) {
			/* Input pin */
			DDRB &= ~(1 << vCapacitorDriverPins[i]);
			/* Tri-state (no pull up) */
			PORTB &= ~(1 << vCapacitorDriverPins[i]);
		capacitorDriverPin = vCapacitorDriverPins[pinCounter];
		resistorValue = vResistorValues[pinCounter];
		/* Set new output port and turn it low */
		DDRB |= (1 << capacitorDriverPin);
		PORTB &= ~(1 << capacitorDriverPin);
		/* Prescan */
		/* Set data acquisition parameters */
		dac.SetAcquisitionEngine(adcChannel, refVoltage, 128.0E+3, samples); 
		/* Start charging capacitor */
		PORTB |= (1 << capacitorDriverPin);
		/* Wait for full charge */
		while(dac.GetSingleData() < fullChargeAdcCounts){}; 
		/* Start discharging capacitor */
		PORTB &= ~(1 << capacitorDriverPin); 	
		/* Sense upper triggering level */
		while(dac.GetSingleData() > upperAdcCounts){}; 
		/* Time stamp and mark event */
		uint32_t start = micros();
		PORTB &= ~(1 << SYN_PIN); /* Comment in normal operation mode */
		/* Sense lower triggering level */
		while(dac.GetSingleData() > lowerAdcCounts){}; 
		/* Time stamp and mark event */
		uint32_t stop = micros();
		PORTB |= (1 << SYN_PIN); /* Comment in normal operation mode */
		/* Compute scanning parameters */
		estimatedTimeConstant = ((stop - start) / 1.0E6) / 2.0;
	} while ((estimatedTimeConstant

Constants and varaible are set appart from the code in the header of the skectch

/* Constants */
const double slowestSamplingRate = 1.0;
const double fastestSamplingRate = 128.0E+3;
const uint16_t samples = 8; 
const double lowestScanTime = (samples / fastestSamplingRate);
const double largestScanTime = (samples / slowestSamplingRate);
const uint16_t refVoltage = ADC_REF_VOL_DEFAULT;  
const double adcReference = 5.0;
const double upperVoltage = 4.5;
const double fullChargeVoltage = (upperVoltage * 1.05);
const double lowerVoltage = (upperVoltage * 0.1353); /* 2 x Time constant */
/* Variables */
double samplingFrequency;
uint16_t adcChannel = 0; /* From 0 to 5 on ATmega328 powered Arduinos */
uint16_t fullChargeAdcCounts;
uint16_t upperAdcCounts;
uint16_t lowerAdcCounts;
uint8_t vCapacitorDriverPins[] = {PINB0, PINB1, PINB2};
double vResistorValues[] = {2.20E+2, 2.20E+4, 2.20E+6};
uint8_t capacitorDriverPin;
double resistorValue;
double capacitanceOffset = 35.0E-12;

And here is a screen capture from a DSO showing a short pulse corresponding to the two attempts from the prescaler to compute the optimal resistance, followed by the final measurement (yellow trace). The blue trace corresponds to the synchronisation pin signal.


  1. pfeerick says:

    You mentioned above that the LM358 was not the best opamp to use, and that low noise amps would be better. Do you have any suggestions as to what would be a better part?

  2. pfeerick says:

    Duh! Thanks, should have remembered you discussed that earlier! That was exactly what I was looking for. Unless I missed something, it’s only the MAX495 that would be a problem, the MAX492 and MAX494 are still in active production. From initial looks, I think the Linear Technology LTC1152 part would be a suitable alternative, although I think I’ll get some MAX492 parts.

Leave a Reply

You must be logged in to post a comment.