Gas station without pumps

2017 February 5

Units matter

Filed under: Circuits course — gasstationwithoutpumps @ 11:37
Tags: , , , , ,

I was a little surprised by how many students had trouble with the following homework question, which was intended to be an easy point for them:

Estimate C2(touching) − C2(not touching), the capacitance of a finger touch on the packing-tape and foil sensor, by estimating the area of your finger that comes in contact with the tape, and assume that the tape is 2mil tape (0.002” thick) made of polypropylene (look up the dielectric constant of polypropylene on line). Warning: an inch is not a meter, and the area of your finger tip touching a plate is not a square meter—watch your units in your calculations!

Remember that capacitance can be computed with the formula C = \frac{\epsilon_r\epsilon_0 A}{d}~,
where \epsilon_r is the dielectric constant,  \epsilon_0=8.854187817E-12 F/m is the permittivity of free space, A is the area, and d is the distance between the plates.

The problem is part of their preparation for making a capacitance touch sensor in lab—estimating about how much capacitance they are trying to sense.

There is a fairly wide range of different correct answers to this question, depending on how large an area is estimated for a finger touch. I considered any area from 0.5 (cm)2 to 4 (cm)2 reasonable, and might have accepted numbers outside that range with written justification from the students.  Some students have no notion of area, apparently, trying to use something like the length of their finger times the thickness of the tape for A.

People did not have trouble looking up the relative dielectric constant of polypropylene (about 2.2)—it might have helped that I mentioned that plastics were generally around 2.2 when we discussed capacitors a week or so ago.

What people had trouble with was the arithmetic with units, a subject that is supposed to have been covered repeatedly since pre-algebra in 7th grade. Students wanted to give me area in meters or cm (not square meters), or thought that inches, cm, and m could all be mixed in the same formula without any conversions.  Many students didn’t bother writing down the units in their formula, and just used raw numbers—this was a good way to forget to do the conversions into consistent units.  This despite the warning in the question to watch out for units!

A lot of students thought that 1 (cm)2 was 0.01 m2, rather than 1E-4 m2. Others made conversion errors from inches to meters (getting the thickness of the tape wrong by factors of 10 to 1000).

A number of students either left units entirely off their answer (no credit) or had the units way off (some students reported capacitances in the farad range, rather than a few tens of picofarads).

A couple of students forgot what the floating-point notation 8.854187817E-12 meant, even though we had covered that earlier in the quarter, and they could easily have looked up the constant on the web to figure out the meaning if they forgot.  I wish high-school teachers would cover this standard way of writing numbers, as most engineering and science faculty assume students already know how to read floating-point notation.

Many students left their answers in “scientific” notation (numbers like 3.3 10-11 F) instead of using more readable engineering notation (33pF). I didn’t take off anything for that, if the answer was correct, but I think that many students need a lot more practice with metric prefixes, so that they get in the habit of using them.

On the plus side, it seems that about a third of the class did get this question right, so there is some hope that students helping each other will spread the understanding to more students.  (Unfortunately, the collaborations that are naturally forming seem to be good students together and clueless students together, which doesn’t help the bottom half of the class much.)

Advertisements

2016 July 13

Autoranging capacitance meter using TeensyLC

Filed under: Circuits course — gasstationwithoutpumps @ 09:59
Tags: , , , ,

 

My earlier post, Capacitance meter using touchRead(), showed the beginnings of a capacitance meter, using the touchRead() routine in the Teensyduino environment.

Today I’ll share a slightly more complicated program that uses the TSI (touch sensing input) peripheral more directly to make an autoranging capacitance meter that can measure capacitors down to 1pF and up to 3µF.  The repeatability of the measurements is not great (about ±3%), but the linearity seems pretty good.

The program includes the ability to zero out the test fixture (important for measuring small capacitances) and to calibrate the meter to a known capacitor.  I don’t have a capacitor with a tight tolerance, so I had to do my calibration against a DT-9205A multimeter, which is not a very reliable standard.  Still, it seemed more consistent than the labeling on the cheap ceramic capacitors I have, some of which seem to be off by a factor of two!

The multimeter could not measure capacitances at the low end of the range, so only nominal values are used there.

The multimeter could not measure capacitances at the low end of the range, so only nominal values are used there.

A number of the capacitors in the 100pF–10nF range seemed to drift consistently upward for quite a while, both with the multimeter and my homebrew capacitance meter. Touching them with my finger brought them sharply back down again, but touching them with stainless steel tweezers did not. I suspect that the effect is a thermal one, with the capacitance dropping when my fingers warmed the capacitors and going up again as the capacitors cooled to room temperature. The change was several percent, which is consistent with a low-quality “Y” dielectric.

// Preliminary program for a capacitance meter
// Kevin Karplus
// 2016 Jul 13


// To use:
//	* Connect a serial monitor (like the Arduino IDE) to the USB port
//	* Connect capacitor to measure between pin 0 and ground
//	* Press "a" and <return> to make an autorange measurement.

// Capacitances in the range 1pF to 3uF can be measured, but large
// capacitors take a long time to measure, up to 9s for 3uF.
// The measurement time can be reduced by reducing NUM_READS, perhaps
//	replacing the constant by an array dependent on range.
//	A factor of about 10 reduction in measurement time is available, as
//	NUM_READS is currrently set to 20.

// Note: readings may drift by +- 5%.  I've not yet determined the
// cause of this drift.  It may be thermal (cooling from finger
// temperature to room temperature may raise the capacitance by 2%,
// depending on the additives to the ceramic used as the dielectric).

// To calibrate:
//	* Remove capacitors from pin 0
//	* Type 'z' to measure the counts for the empty test fixture
//	* Pick a known capacitor (around 1nF) and connect it between
//		pin 0 and ground.  The capacitor must be small enough
//		to be measurable on the highest-resolution range (<=3nF).
//		To calibrate with a larger capacitor, first calibrate with
//		a small one to get the highest-resolution range set, then
//		repeat the calibration with the larger capacitor, which
//		will only reset the larger ranges.
//	* Type 'k', followed by an integer known capacitance in pF,
//		followed by a separator (like ';')
//	* Type 'c', to measure the pF per count

// Note: the calibrations are reported to the USB serial port, so they
// can be saved and used to edit the source code, changing count_for_0pF
// and pf_per_count arrays.

// The measurement process can be followed in more detail by turning
// on debugging with the 'D' command.
// Debugging can be turned off with the 'd' command.

// Capacitance is measured on pin 0 (one of several pins with TSI),
// because pin 0 is immediately next to ground, making it easy to connect
// small capacitors between pin 0 and ground.
#define TOUCH_PIN (0)

// forward references to later routines
int32_t capRead(uint8_t pin, 
		uint8_t I_ext=3, uint8_t I_ref=4, 
		uint8_t Prescale=2, uint8_t N_scan=9);
float autoCapRead(uint8_t pin);
int readInt(void);
void print_calib(int count, int n_cycles=1);
float read_counts_per_cycle(uint8_t i_ext, uint8_t i_ref,float count_for_zero);

volatile uint8_t debug;

// The current for the external oscillator and the reference
// oscillator of the TSI interface determine the capacitance range, resolution,
// and speed of the measurement.
// The program uses the highest-resolution range that does not cause
// the 16-bit counter to overflow.

// Settings of the current paramters for the different ranges
// The highest-resolution range is first, the widest range is last.
#define NUM_RANGES (6)
const int8_t i_ext_choices[NUM_RANGES] = {2,3,4,5,6,7};
const int8_t i_ref_choices[NUM_RANGES] = {5,4,3,2,1,0};

float pF_per_count[NUM_RANGES]=
	{0.2126, 0.772, 2.897, 11.08, 47., 195.3};
float count_for_0pF[NUM_RANGES] =
	{48.7612, 13.82, 3.813, 1.0605, 0.273, 0.0517};

// parameters for calibration
int C_known=0;	// known capacitance value 

void setup() 
{
    pinMode(TOUCH_PIN, INPUT);
    Serial.begin(115200);
    debug=0;
}

void loop()
{
     if (Serial.available())
     {   char c=Serial.read();
         if (c=='D') {debug=1;}
	 else if (c=='d') {debug=0;}
	 else if (c=='a')
         {   // do one reading and print it
             Serial.print(autoCapRead(TOUCH_PIN));
             Serial.println(" pF");
        }
        else if (c=='k')
        {   // set known capacitance for calibration checks
	   C_known=readInt();
        }
	else if (c=='z')
	{   // Set the count_for_0pF array for each range.
	    // Print the current parameters and zero count for each range.
	    // Only issue this command if there is no capacitor connected
	    // to TOUCH_PIN
	    for (int range=0; range<NUM_RANGES; range++)
	    {   uint8_t i_ext=i_ext_choices[range];
	        uint8_t i_ref=i_ref_choices[range];
		count_for_0pF[range] =read_counts_per_cycle(i_ext,i_ref,0.0);
		Serial.print("# zero for ");
		Serial.print(i_ext); Serial.print("\t");
		Serial.print(i_ref); Serial.print("\t");
		Serial.println(count_for_0pF[range],4);
	    }	
	}
	else if (c=='c')
	{   // do autocalibration, setting pF_per_count
	    // printing known capacitance, 
	    //		external current setting,
	    //		reference current setting,
	    //		average pF_per_count for a single cycle
	    
	    if (C_known==0)
	    {   Serial.println("# use 'k<known C in pF>;' first");
	    	return;
	    }
	    for (int range=0; range<NUM_RANGES; range++) { uint8_t i_ext=i_ext_choices[range]; uint8_t i_ref=i_ref_choices[range]; float calib=read_counts_per_cycle(i_ext,i_ref,count_for_0pF[range]); if (!isnan(calib)) { calib = C_known/calib; pF_per_count[range]= calib; } else { Serial.print("# "); // comment out overflows } Serial.print(C_known); Serial.print("\t"); Serial.print(i_ext); Serial.print("\t"); Serial.print(i_ref); Serial.print("\t"); Serial.println(calib,4); } } } } // print a calibration line void print_calib(int count, uint8_t i_ext, uint8_t i_ref, int n_cycles) { float avg_count = (count+0.0)/n_cycles; Serial.print(C_known); Serial.print("\t"); Serial.print(i_ext); Serial.print("\t"); Serial.print(i_ref); Serial.print("\t"); Serial.print(count); Serial.print("\t"); Serial.print(n_cycles); Serial.print("\t"); Serial.println(C_known/avg_count,4); } float read_counts_per_cycle(uint8_t i_ext, uint8_t i_ref, float count_for_zero) { // Do one measurement and return average counts/cycle - count_for_zero. // If debug set, print C_known, count, i_ext, i_ref, n_cycles, cap/count // // Actually does NUM_READS+1 measurements: one with a single cycle, // then again NUM_READS times with as many cycles as can be fit without // overflowing counter. if (debug) { Serial.println("# C\ti_ext\ti_ref\tcount\tn_cycle\tC/avg_count"); } int count=capRead(TOUCH_PIN, i_ext, i_ref, 0, 0); if (count>=0xFFFF)
    {   if (debug)
    	{   Serial.print("# "); // comment out overflows
	    print_calib(count,i_ext,i_ref,1);
	}
	return NAN;	// abort rest of calibration check
    }

    // Determine max number of cycles that can fit
    int cycles= 0xFFFE/count; // how many cycles to use
    int prescale,n_scan;
    for (prescale=0; cycles>32 && prescale<7; prescale++) {cycles/=2;} if (cycles==0) {n_scan=0;} else if (cycles>32) {n_scan=31;}
    else {n_scan=cycles-1;}
    cycles = (n_scan+1)<<prescale;

#define NUM_READS	(20)
    // To do: replace NUM_READS with a const uint8_t num_reads[NUM_RANGES]
    // array, to speed up measurement at high ranges.

    int sum_count=0;
    for (int i=0; i<NUM_READS; i++) { count=capRead(TOUCH_PIN, i_ext, i_ref, prescale, n_scan); if (debug) { print_calib(count, i_ext, i_ref, cycles); } sum_count+=count; } return (sum_count+0.0)/ (NUM_READS*cycles) -count_for_zero; } // Read a non-negative integer from Serial as a series of digits, terminated // by any non-digit (recommend using something obvious like ';'). // The terminating character is discarded. int readInt(void) { int value=0; while (!Serial.available()) {} // wait for next char char c=Serial.read(); while (c>='0' && c<='9')
    {   value= 10*value + (c-'0');
	while (!Serial.available()) {}	// wait for next char
       	c=Serial.read();
    }
    return value;
}
   


float autoCapRead(uint8_t pin)
{
    // Return the capcitance in pF at the pin, using the highest-resolution
    // range that doesn't overflow the counter.
    
    // pick the lowest (highest resolution) range that doesn't overflow
    int32_t count;	// number of counts of ref oscillator
    int range;
    for (range=0, count=0xFFFF;  range<NUM_RANGES && count>=0xFFFF; range++)
    {	count=capRead(pin, i_ext_choices[range],i_ref_choices[range],0,0);
    }
    range--;
    
    if (count>=0xFFFF)
    {    return NAN;	// capacitance too big to measure with TSI
    }
    
    float count_per_cycle=read_counts_per_cycle(i_ext_choices[range],
		i_ref_choices[range],
		count_for_0pF[range]);
    return pF_per_count[range]*count_per_cycle;
}


/* Raw reading is based on the
 * Teensyduino Core Library touch.c (which implements touchRead)
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2013 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be 
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows 
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// #include "core_pins.h"

// capacitance is supposed to be
//	Cref * (1<<I_ext) / ( (1<<I_ref) * (1<<Prescale) * (N_scan+1) ) * COUNT
// but use 
// Cref * ext_current[I_ext] / ( ref_current[I_ref] * (1<<Prescale) * (N_scan+1) ) * COUNT // because the current ratios are not a constant factor of 2 // with DVOLT==0 (slowest, but least noise sensitive), // I_ext = I_ref-1, // Prescale=2, // N_scan=9, // Capacitance is approx 0.01846 pF * COUNT, // so Cref approx 1.47694pF #if defined(__MK20DX128__) || defined(__MK20DX256__) // These settings give approx 0.02 pF sensitivity and 1200 pF range // Lower current, higher number of scans, and higher prescaler // increase sensitivity, but the trade-off is longer measurement // time and decreased range. static const uint8_t pin2tsi[] = { //0 1 2 3 4 5 6 7 8 9 9, 10, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 6, 8, 7, 255, 255, 14, 15, 255, 12, 255, 255, 255, 255, 255, 255, 11, 5 }; #elif defined(__MK66FX1M0__) static const uint8_t pin2tsi[] = { //0 1 2 3 4 5 6 7 8 9 9, 10, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 6, 8, 7, 255, 255, 14, 15, 255, 255, 255, 255, 255, 11, 12, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; #elif defined(__MKL26Z64__) static const uint8_t pin2tsi[] = { //0 1 2 3 4 5 6 7 8 9 9, 10, 255, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 6, 8, 7, 255, 255, 14, 15, 255, 255, 255 }; #endif // for I_ref=I_ext+1, N_scan=9, Prescale=2, output is approx pF * 50 // time to measure 33 pF is approx 0.25 ms // time to measure 1000 pF is approx 4.5 ms int32_t capRead(uint8_t pin, uint8_t I_ext, uint8_t I_ref, uint8_t Prescale, uint8_t N_scan) { uint32_t ch; int32_t count; if (pin >= NUM_DIGITAL_PINS) return 0;
    ch = pin2tsi[pin];
    if (ch == 255) return 0;

    *portConfigRegister(pin) = PORT_PCR_MUX(0);
    SIM_SCGC5 |= SIM_SCGC5_TSI;
#if defined(KINETISK)
    TSI0_GENCS = 0;
    TSI0_PEN = (1 << ch);
    TSI0_SCANC = TSI_SCANC_REFCHRG(I_ref) | TSI_SCANC_EXTCHRG(I_ext);
    TSI0_GENCS = TSI_GENCS_NSCN(N_scan) | TSI_GENCS_PS(Prescale) | TSI_GENCS_TSIEN | TSI_GENCS_SWTS;
    delayMicroseconds(10);
    while (TSI0_GENCS & TSI_GENCS_SCNIP) ; // wait
    delayMicroseconds(1);
    count= *((volatile uint16_t *)(&TSI0_CNTR1) + ch);
#elif defined(KINETISL)
    TSI0_GENCS = TSI_GENCS_REFCHRG(I_ref) | TSI_GENCS_EXTCHRG(I_ext) | TSI_GENCS_PS(Prescale)
	    | TSI_GENCS_NSCN(N_scan) | TSI_GENCS_TSIEN | TSI_GENCS_EOSF;
    TSI0_DATA = TSI_DATA_TSICH(ch) | TSI_DATA_SWTS;
    delayMicroseconds(10);
    while (TSI0_GENCS & TSI_GENCS_SCNIP) ; // wait
    delayMicroseconds(1);
    count= TSI0_DATA & 0xFFFF;
#endif
    if (debug)
    {
	Serial.print("  I_ext= ");  Serial.print(I_ext);
	Serial.print("  I_ref= ");  Serial.print(I_ref);
	Serial.print("  Prescale= ");  Serial.print(Prescale);
	Serial.print("  N_scan= ");  Serial.print(N_scan);
	Serial.print("  count= ");  Serial.print(count);
	Serial.println();
    }
    return count;
}

2016 July 11

Capacitance meter using touchRead()

Filed under: Circuits course — gasstationwithoutpumps @ 12:57
Tags: , ,

I decided to see whether the TSI (touch sensing input) on the Teensy boards was good enough to use as a capacitance meter. My first attempt was to use the touchRead() interface from the Teensyduino environment.

The test code was very simple:

#define LED_PIN  (13)
#define TOUCH_PIN (0)

void setup() 
{
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW); 
    pinMode(TOUCH_PIN, INPUT);
    Serial.begin(115200);
}

void loop() 
{
     if (Serial.available())
     {   char c=Serial.read();
         if (c=='1')
         {   int sum_touch=0;
             for(int i=100; i>0; i--)
             {   sum_touch+=touchRead(TOUCH_PIN);
                 digitalWrite(LED_PIN, HIGH);
                 delay(20);
                 digitalWrite(LED_PIN, LOW); 
             }
             Serial.println(sum_touch);
         }
     }
}

I just read the pin with touchRead 100 times, adding the results. I measured several different capacitors to find out the range of readings and to fit a function to the data.

The way the touch sensing works is that there are two hysteresis oscillators in the microcontroller, one using a fixed reference capacitance and the other using the capacitance of a pin.  The feedback in each oscillator is not a resistor, but switches between a positive and a negative constant-current source. This should give the input to the Schmitt trigger a clean triangle wave.  There are 8 different constant-current sources to choose from, and different sources can be chosen for the two oscillators.  The amount of hysteresis can also be chosen.  The pin-controlled oscillator output is divided down by a pair of counters (one that can be set to 1, 2, 4, 8, …, 128, the other to  1, 2, 3, 4, … , 32), and the number of pulses of the reference oscillator is counted for one tick of the divided-down pin-controlled oscillator.  The touchRead() function fixes which current sources, which hysteresis voltage, and which counter settings are used, so that the reading is directly proportional to the period of the pin-controlled oscillator, which in turn is directly proportional to the capacitance.

Therefore, the readings should be linear with the capacitance, but there is likely to be some parasitic capacitance that needs to be added (indeed, I get a reading of around 551.7 when the Teensy board is plugged into a bread board, with no deliberate capacitance is added).  So the model I want to fit is y= a (C + C_{0}).

The simple linear fit measures each capacitor within about 10% of its nominal value, which may be as accurate as the capacitors are (these were cheap ceramic assortments from China, with no specs).

The simple linear fit measures each capacitor within about 10% of its nominal value, which may be as accurate as the capacitors are (these were cheap ceramic assortments from China, with no specs).

To get good fits at the low end, I fit the C0 parameter only for the data points at 0pF, 1pF, and 5pF, but fit the scaling parameter over the whole range. It is not visible on the plot, but each capacitor was measured multiple times, and the variation in measurement was less than ±0.3%.  Of course, this is for averages of 100 readings, so the raw readings may vary up to ±3%—I’ve not tested for variation.  Somewhat surprisingly, the % variation seemed to be larger at the high end than at the low end—I would have expected the high end to have less variation (because of reduced quantization error).

I checked the capacitance of a small aluminum foil and packing tape touch pad, like we use in the applied electronics course.  It was about 7.05pF when not touched, and 60.8pF when touched with moderate pressure.

Because touchRead() reports 65535 for any capacitance that is too large, the maximum measurable capacitance is 65534/55.3942 – 10.1921 pF = 1173pF. If I want to measure large capacitors, I’ll need to rewrite the touchRead() code to use the TSI functions of the microcontroller more directly. I’ve started work on that, but I’ll save it for a later post. I expect to be able to measure up to at least 1µF. That code will also include translation to standard capacitance units and calibration options.

2015 July 21

Measuring PteroDAQ KL25Z input impedance

In a series of posts (most recently Measuring BitScope BS-10 input impedance), I’ve been measuring the input impedance of my various ways of measuring AC voltage:

meter Z
Radio Shack 10.87MΩ || 18.54pF
DT-830B 0.42MΩ || 31.59pF
DT-9205A 13MΩ || 22pF
BitScope BS-10 oscilloscope 1.025MΩ || 9.8pF

I spent yesterday trying to add the PteroDAQ data acquisition system with the Freedom KL25Z board to that list.

One problem was that PteroDAQ was not designed to report an RMS voltage, but just a waveform, so I modified PteroDAQ to report the mean and standard deviation of a channel (not in the released version yet, as I still have some work to do on the user interface). Note that the mean of a channel is its DC bias, and the standard deviation is the RMS AC voltage. (I’ve never much cared for meters that report RMS AC+DC, which is the root-mean-square voltage without separating AC and DC components.)

A bigger problem is that PteroDAQ can only sample at fairly low frequencies, but the parallel capacitance is expected to be fairly small (pin capacitance for the pin is only about 7pF and the short wiring on the board should only add another couple of picofarads), so the RC time constants will be small. The result is that the low frequencies below the Nyquist frequency will not be much affected by the parallel capacitance, and all I would be able to estimate is the DC resistance of the inputs.

I can take advantage of a trick, however, to get effectively much higher sampling rates: aliasing. Because the input is a sine wave of stable frequency, f, I can sample it at every \frac{n+\phi}{f} seconds and get a waveform that advances by phase \phi. I can pick the integer n to be large enough to get a feasible sampling rate while still seeing the whole waveform, especially if I pick the phase advance to be about \phi=\pi/128, so that I see all the 256 entries in the function generator’s table.

This trick has the further advantage of presenting the sample-and-hold with about the same value as it sampled on the previous sample, so that I don’t have to worry about the short sampling time not getting fully charged through a high-impedance input.  If I don’t do the aliasing trick, then the short sample time PteroDAQ uses (4 cycles of a 6MHz clock, or 667ns) is not enough to charge the sampling capacitor to the final voltage.

At higher frequencies, even this short sampling time is too long—at 1MHz the voltage changes substantially in 667ns, and the sampling capacitor ends up averaging the value over the sampling interval, which reduces the AC RMS voltage.

I made my measurements with the hardware averaging set to 1×, since averaging multiple readings is a digital low-pass filter that would hide the analog low-pass filter I’m trying to measure.  Because the measurements at 1× are so noisy, I took a large number of  measurements to determine the mean and standard deviation.  The results are still a bit noisy, as I did not realize the importance of having very precise sampling rates initially—if the \phi value is too small, then I have to be careful to include an integer number of periods of the aliased waveform in the averaging to avoid bias, and if it is too large, then the short sample time is not long enough to charge fully and my waveform is not full scale.  A good compromise seems to be to pick n so that the sampling rate is around 5kHz and \phi=\pi/128 to get about a 19.5Hz aliased waveform. Only a few of my measurements were done with these settings, so I should probably redo the whole set at some point.

The aliasing trick is not a perfect one—at high frequencies there are a lot of glitches, where it is clear that the sampling did not happen at precisely the place in the waveform desired. This is probably due to jitter in the digital phase oscillators used in FG085, as the PteroDAQ interrupts should come at precise intervals (though the intervals may not be at exactly the frequency desired). The noise is much more of a problem with a high impedance source, as it may take several samples for the sampling capacitor to get back to the correct value.

I measured PTB0 with 1× sampling both directly driven by the FG085 (with 2.9Vpp and +1.8V offset) and through a 100.1kΩ resistor.

The dropoff in voltages at high frequencies with not series resistor is probably due to the averaging of the 667ns sampling time.

The dropoff in voltages at high frequencies with not series resistor is probably due to the averaging of the 667ns sampling time.

The impedance estimate derived from these measurements is pretty solid on the DC resistance, but the parallel capacitance estimate varies depending on how much of the high-frequency measurement I use in the fitting.

My estimate of C is 8pF±2pF, depending on how much of the high frequency data I include in the fit.

My estimate of C is 8pF±2pF, depending on how much of the high frequency data I include in the fit.

Estimating the input impedance of the single-ended pins of KL25Z at 2.5MΩ || 8pF seems pretty good. I’ll have to check the differential inputs separately, as there is no reason to suppose that they have the same input impedance.

I think that the 8pF I’m seeing is mainly the pin capacitance of the PTB0 pin, with a little extra board capacitance. The sampling capacitor is not really measurable here, since it is only connected to the input for very short intervals. To measure the RC time constant of the sample-and-hold circuit, we’d have to vary the sampling time (which is possible on the KL25Z, but which PteroDAQ is not set up to do).

2015 July 18

Measuring BitScope BS-10 input impedance

In Voltmeter impedance I presented a 2-voltmeter way of measuring AC voltmeter impedance, and in Measuring voltmeter input impedance I presented a way of measuring the AC voltmeter impedance with just the voltmeter itself, a function generator, and a resistor that is around the DC resistance of the meter (or a little smaller).

I got measurements of

meter Z
Radio Shack 10.87MΩ || 18.54pF
DT-830B 0.42MΩ || 31.59pF
DT-9205A 13MΩ || 22pF

I decided to apply the same techniques to the BitScope BS-10 USB oscilloscope, getting the following result:

The BitScope provides the standard oscilloscope input impedance of 1MΩ.

The BitScope provides the standard oscilloscope input impedance of 1MΩ.

The BitScope has the standard 1MΩ input impedance with a fairly small 10pF parallel capacitance (probably largely from the 20cm leads I was using). The measurements are a bit noisy, because I was using the provided peak-to-peak voltage measurement, which varies quite a bit from trace to trace.  At high frequencies the waveform is not much like a sine wave, so the results are bit dubious that far out—I did not include frequencies greater than 100kHz in the fit.

The voltage measurements look pretty good, though getting consistent measurements from BitScope’s peak-to-peak measurements with the cursor is a bit difficult:

I think that the voltage drop with no series resistor is from the limits of the FG085 function generator, not from the BitScope oscilloscope.

I think that the voltage drop with no series resistor is from the limits of the FG085 function generator, not from the BitScope oscilloscope.

I should probably average hundreds of waveforms to get a more precise and accurate measurement, but setting that up would be tedious.   I did gather 576 traces of the 200kHz waveform and averaged them together to get a 2.2334V peak-to-peak waveform that looks much more like a sine-wave than I would have expected from the individual traces:

The distortions from a sine wave are barely visible here.

The distortions from a sine wave are barely visible here.

The BitScope is capable of seeing the glitches in the waveform at lower frequencies, like 2kHz, but only barely. The nonlinearities are much better viewed with the PteroDAQ running at lower frequencies.

Next Page »

Blog at WordPress.com.

%d bloggers like this: