Posts with «circuits course» label

Still better I-V plot for Schottky diodes

Last weekend I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode, using the following setup

and measuring the results with an Arduino Leonardo.  I claimed that the resulting data fits the model well for over six decades (> 120dB):

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.
click to embiggen

I was a little dissatisfied at the low end, because of the low resolution of the Arduino analog-to-digital converter (only 10 bits).  This weekend I decided to repeat the measurements, but using a Freedom KL25Z board, which has a 16-bit ADC.  Of course, it doesn’t really get 16 bits of accuracy—the data sheet claims that when you use the hardware averaging of 32 samples in 16-bit differential mode (the most accurate) you get at least 12.8 equivalent bits and typically 14.5 equivalent bits. (For single-ended 16-bit, the effective number of bits is only guaranteed to be 12.2, and the typical is 13.9 bits.)  They claim a ±6.8LSB total unadjusted error.

My son helped me get the bare metal ARM system set up on my laptop, along with ADC and UART routines, so that I could write my own single-purpose data logger for this problem (he’s working on getting the KL25Z board integrated into the Arduino Data Logger, but it isn’t close to being ready yet).  My program used the longest sample times and hardware averaging of 32 samples, to get the most accurate conversions possible from the 16-bit ADC.  The first version of the program used differential inputs for the voltage across the diode (E20-E21), but single-ended readings (E21) for the voltage across the resistor.  I had to reduce the voltage for the test from 5v to 3.3v, because the KL25Z runs on 3.3v, not 5v. I got some rather weird results:

Two runs of measurement with R2=100Ω. The low-current measurements seem to be all noise.
click to embiggen

I could get decent measurement in the low-current range by using a larger resistor, so the problem was not noise in the measurement fixture or problems reading low differential voltages on the diode, but just with the small single-ended read for the current. It is pretty clear to me that the ADC does not work well when the input voltage results in less than about 50 counts.  (Note, that means that at the low end of the voltage range you only have about a 9.4-bit equivalent ADC.)

I modified the circuit to allow differential reading away from 0 for both the voltage across the diode and the voltage across the shunt resistor:

Adding an extra resistor ensured that the lowest voltage did not get too close to ground and I could use differential reads for both voltage (E20-E21) and current (E22-E23)/R2

This gave me a much cleaner reading, with problems only once the differential counts got below about 20:

Differential measurement with R2=100Ω. The low-current measurements have problems when the counts get small, but not nearly as severely as with the single-ended measurements.
click to embiggen

I replaced the 100Ω R2 resistor with a 15.56kΩ resistor (nominally 15kΩ), to extend to lower currents despite the noise in the ADC:

This plot extends the fit down to about 0.1µA, but only by adding an extra term—an offset to the voltage. I thought at first that overshoot to –11mV is an error in the analog-to-digital converter on the KL25Z, as I couldn’t see how my circuit could be back-biasing the diode.
Click to embiggen

I tried using larger resistors, but was unable to get any better data using them—I seem to be limited by the differential voltage measurement of the diode at the low end. I thought I might be able to improve the measurements by adding an instrumentation amp to increase the signal for low voltages.  But first I tried just hooking up a voltmeter, with no ADC or instrumentation amp connections.  When the voltage across R2 (100kΩ) is 0.31mV, the voltage across the diode plus R2 is only 0.05V, so there is -0.26mV across the diode.  The backwards voltage across the diode was not an artifact of the ADC!

I then tried looking at the voltage across the diode with my oscilloscope.  There is about 20mV of AC noise, independent of the DC voltage, until the diode has about 50mV across it (with the 15.5kΩ resistor for R2), by which time the noise has dropped to about 10mV (the Bitscope oscilloscope with the differential probe has a noise floor of about 3mV, if the two leads are connected together, so this is not just oscilloscope noise).  This noise seems to be white noise, not 60Hz hum pickup, so is probably coming from the diode.  This AC noise signal limits how accurately we can measure the DC current, and rectifying the noise could be the source of the mysterious “backwards” bias.

To reduce the noise, I put a 4.7µF ceramic capacitor in parallel with the diode, and redid all the measurements with 100Ω, 15.5kΩ, and 100kΩ resistors for R2.

Modified measurement circuit, adding a bypass capacitor to reduce AC noise on the diode and allow better DC measurement.

Now the signals are very clean down to nanoamp levels. I no longer need to add an offset to the voltage, as it is 0 to within the measurement repeatability. The noise from very small voltage differences for the 100Ω shunt resistor is still a bit of a problem, but that region is well covered by 15.5kΩ data. The curve was fit using just the 15.5kΩ and 100kΩ data, to avoid having to trim out the noise from the 100Ω data.
Click to embiggen

Lessons learned today:

  • Higher-resolution ADCs do give smoother curves, with less digitization noise, but they aren’t a panacea for measurement problems. To get most of the resolution, I had to set the ADC to use long sample times and do a lot of averaging. I understand that Freescale Kinetis M series include 24-bit sigma-delta converters for higher precision at much lower speed (24 bits is 7 decimal digits), as well as the high-speed 16-bit successive-approximation converters. Unfortunately, they don’t have a low-cost development board for this series.
  • Stay away from the bottom end of the ADC range on the KL25Z.  Scale single-ended inputs to have values at least 50, and differential inputs to have values at least 20.  There may be similar problems at the top end of the range, but I did not test for them.
    I wondered if the problem may be switching from the large value for the voltage across the diode to the small voltage across the shunt resistor that was the problem. I tried putting in a dummy read between the voltage and the current reads, but that didn’t help at all. At first I thought that the low-count readings were good with the large shunt resistors, but this is probably an illusion: errors in the current measurement for small currents aren’t visible on the plot, because the voltage across the diode is not changing, and so large horizontal errors in the plot are not visible there.
  • Watch out for AC noise when trying to measure DC parameters.  If there are semiconductor junctions around, the noise may be rectified to produce an unwanted DC signal.
  • The differential ADC settings have a range of ±VDDA, not ±VDDA/2. This means that the least-significant bit step size is twice as big for differential inputs as for single-ended inputs. For some reason the Freescale documentation never bothers to express what the differential range is.
  • Serial USB connections are a bit flakey—the Arduino serial monitor missed a byte about every 200–300 lines.  I looked for anomalous points on the plot, then commented out the lines that produced them—they were almost all explainable by one character having been missed by the serial monitor; e.g., I commented out “662401069     86      19″ right after “660001069       865     17″,  because the last digit of the voltage (the second field) was missing.  The fields were a timestamp (in 24MHz ticks), voltage across the diode (in ADC units), and voltage across the shunt resistance (in ADC units).  [Actually, this was not a new lesson for me—I've had to do the same on almost all files collected from the Arduino serial monitor.  My son's data logger code is better at not losing data, but it is still worthwhile to check for anomalies.]
  • The 3.3v supply from the Freedom board is much cleaner than the 5v USB supply that I get from the Arduino (unless I use an external power supply with the Arduino), but I can only take about 10mA from the 3.3v supply before it begins to droop.  If I want  more than that, I’d better provide my own power supply (or at least my own LDO regulator from the USB 5v supply).

Filed under: Circuits course, Data acquisition Tagged: Arduino, i-vs-v-plot, KL25Z, Schottky diode

Still better I-V plot for Schottky diodes

Last weekend I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode, using the following setup

and measuring the results with an Arduino Leonardo.  I claimed that the resulting data fits the model well for over six decades (> 120dB):

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.
click to embiggen

I was a little dissatisfied at the low end, because of the low resolution of the Arduino analog-to-digital converter (only 10 bits).  This weekend I decided to repeat the measurements, but using a Freedom KL25Z board, which has a 16-bit ADC.  Of course, it doesn’t really get 16 bits of accuracy—the data sheet claims that when you use the hardware averaging of 32 samples in 16-bit differential mode (the most accurate) you get at least 12.8 equivalent bits and typically 14.5 equivalent bits. (For single-ended 16-bit, the effective number of bits is only guaranteed to be 12.2, and the typical is 13.9 bits.)  They claim a ±6.8LSB total unadjusted error.

My son helped me get the bare metal ARM system set up on my laptop, along with ADC and UART routines, so that I could write my own single-purpose data logger for this problem (he’s working on getting the KL25Z board integrated into the Arduino Data Logger, but it isn’t close to being ready yet).  My program used the longest sample times and hardware averaging of 32 samples, to get the most accurate conversions possible from the 16-bit ADC.  The first version of the program used differential inputs for the voltage across the diode (E20-E21), but single-ended readings (E21) for the voltage across the resistor.  I had to reduce the voltage for the test from 5v to 3.3v, because the KL25Z runs on 3.3v, not 5v. I got some rather weird results:

Two runs of measurement with R2=100Ω. The low-current measurements seem to be all noise.
click to embiggen

I could get decent measurement in the low-current range by using a larger resistor, so the problem was not noise in the measurement fixture or problems reading low differential voltages on the diode, but just with the small single-ended read for the current. It is pretty clear to me that the ADC does not work well when the input voltage results in less than about 50 counts.  (Note, that means that at the low end of the voltage range you only have about a 9.4-bit equivalent ADC.)

I modified the circuit to allow differential reading away from 0 for both the voltage across the diode and the voltage across the shunt resistor:

Adding an extra resistor ensured that the lowest voltage did not get too close to ground and I could use differential reads for both voltage (E20-E21) and current (E22-E23)/R2

This gave me a much cleaner reading, with problems only once the differential counts got below about 20:

Differential measurement with R2=100Ω. The low-current measurements have problems when the counts get small, but not nearly as severely as with the single-ended measurements.
click to embiggen

I replaced the 100Ω R2 resistor with a 15.56kΩ resistor (nominally 15kΩ), to extend to lower currents despite the noise in the ADC:

This plot extends the fit down to about 0.1µA, but only by adding an extra term—an offset to the voltage. I thought at first that overshoot to –11mV is an error in the analog-to-digital converter on the KL25Z, as I couldn’t see how my circuit could be back-biasing the diode.
Click to embiggen

I tried using larger resistors, but was unable to get any better data using them—I seem to be limited by the differential voltage measurement of the diode at the low end. I thought I might be able to improve the measurements by adding an instrumentation amp to increase the signal for low voltages.  But first I tried just hooking up a voltmeter, with no ADC or instrumentation amp connections.  When the voltage across R2 (100kΩ) is 0.31mV, the voltage across the diode plus R2 is only 0.05V, so there is -0.26mV across the diode.  The backwards voltage across the diode was not an artifact of the ADC!

I then tried looking at the voltage across the diode with my oscilloscope.  There is about 20mV of AC noise, independent of the DC voltage, until the diode has about 50mV across it (with the 15.5kΩ resistor for R2), by which time the noise has dropped to about 10mV (the Bitscope oscilloscope with the differential probe has a noise floor of about 3mV, if the two leads are connected together, so this is not just oscilloscope noise).  This noise seems to be white noise, not 60Hz hum pickup, so is probably coming from the diode.  This AC noise signal limits how accurately we can measure the DC current, and rectifying the noise could be the source of the mysterious “backwards” bias.

To reduce the noise, I put a 4.7µF ceramic capacitor in parallel with the diode, and redid all the measurements with 100Ω, 15.5kΩ, and 100kΩ resistors for R2.

Modified measurement circuit, adding a bypass capacitor to reduce AC noise on the diode and allow better DC measurement.

Now the signals are very clean down to nanoamp levels. I no longer need to add an offset to the voltage, as it is 0 to within the measurement repeatability. The noise from very small voltage differences for the 100Ω shunt resistor is still a bit of a problem, but that region is well covered by 15.5kΩ data. The curve was fit using just the 15.5kΩ and 100kΩ data, to avoid having to trim out the noise from the 100Ω data.
Click to embiggen

Lessons learned today:

  • Higher-resolution ADCs do give smoother curves, with less digitization noise, but they aren’t a panacea for measurement problems. To get most of the resolution, I had to set the ADC to use long sample times and do a lot of averaging. I understand that Freescale Kinetis M series include 24-bit sigma-delta converters for higher precision at much lower speed (24 bits is 7 decimal digits), as well as the high-speed 16-bit successive-approximation converters. Unfortunately, they don’t have a low-cost development board for this series.
  • Stay away from the bottom end of the ADC range on the KL25Z.  Scale single-ended inputs to have values at least 50, and differential inputs to have values at least 20.  There may be similar problems at the top end of the range, but I did not test for them.
    I wondered if the problem may be switching from the large value for the voltage across the diode to the small voltage across the shunt resistor that was the problem. I tried putting in a dummy read between the voltage and the current reads, but that didn’t help at all. At first I thought that the low-count readings were good with the large shunt resistors, but this is probably an illusion: errors in the current measurement for small currents aren’t visible on the plot, because the voltage across the diode is not changing, and so large horizontal errors in the plot are not visible there.
  • Watch out for AC noise when trying to measure DC parameters.  If there are semiconductor junctions around, the noise may be rectified to produce an unwanted DC signal.
  • The differential ADC settings have a range of ±VDDA, not ±VDDA/2. This means that the least-significant bit step size is twice as big for differential inputs as for single-ended inputs. For some reason the Freescale documentation never bothers to express what the differential range is.
  • Serial USB connections are a bit flakey—the Arduino serial monitor missed a byte about every 200–300 lines.  I looked for anomalous points on the plot, then commented out the lines that produced them—they were almost all explainable by one character having been missed by the serial monitor; e.g., I commented out “662401069     86      19″ right after “660001069       865     17″,  because the last digit of the voltage (the second field) was missing.  The fields were a timestamp (in 24MHz ticks), voltage across the diode (in ADC units), and voltage across the shunt resistance (in ADC units).  [Actually, this was not a new lesson for me—I've had to do the same on almost all files collected from the Arduino serial monitor.  My son's data logger code is better at not losing data, but it is still worthwhile to check for anomalies.]
  • The 3.3v supply from the Freedom board is much cleaner than the 5v USB supply that I get from the Arduino (unless I use an external power supply with the Arduino), but I can only take about 10mA from the 3.3v supply before it begins to droop.  If I want  more than that, I’d better provide my own power supply (or at least my own LDO regulator from the USB 5v supply).

Filed under: Circuits course, Data acquisition Tagged: Arduino, i-vs-v-plot, KL25Z, Schottky diode

I-vs-V plots for base-emitter diodes

Earlier today I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode:

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.

I also said that I should characterize the base-emitter junction of a PNP and an NPN transistor this way also, for setting the appropriate resistances for the log amplifier in the loudness circuit.  I did that this evening for the S9012 PNP and S9013 NPN transistors:

Base-emitter diode for the S9012 PNP transistor (collector and base connected together). VT is 25.3mV and ISO is 13.4fA.

Characteristics for the base-emitter diode of the S9013 NPN transistor (collector and base connected together). VT is 25.5mV and ISO is 8.85fA.

For both transistors, the region where the logarithmic fit is good is from about 0.5µA to about 50mA (maybe only 35mA for the NPN transistor). That gives about a 100dB working range for a log amplifier, if the largest current corresponds to 50mA. Of course, the op amps that are driving the input of the log amplifier don’t have that much drive capability, and we are probably limited to about 20mA—only a 90dB dynamic range on the log amplifier.


Filed under: Circuits course, Data acquisition Tagged: Arduino, bipolar transistors, i-vs-v-plot, log amplifier

I-vs-V plots for base-emitter diodes

Earlier today I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode:

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.

I also said that I should characterize the base-emitter junction of a PNP and an NPN transistor this way also, for setting the appropriate resistances for the log amplifier in the loudness circuit.  I did that this evening for the S9012 PNP and S9013 NPN transistors:

Base-emitter diode for the S9012 PNP transistor (collector and base connected together). VT is 25.3mV and ISO is 13.4fA.

Characteristics for the base-emitter diode of the S9013 NPN transistor (collector and base connected together). VT is 25.5mV and ISO is 8.85fA.

For both transistors, the region where the logarithmic fit is good is from about 0.5µA to about 50mA (maybe only 35mA for the NPN transistor). That gives about a 100dB working range for a log amplifier, if the largest current corresponds to 50mA. Of course, the op amps that are driving the input of the log amplifier don’t have that much drive capability, and we are probably limited to about 20mA—only a 90dB dynamic range on the log amplifier.


Filed under: Circuits course, Data acquisition Tagged: Arduino, bipolar transistors, i-vs-v-plot, log amplifier

Better I-V plot for Schottky diodes

Yesterday I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode, but I wasn’t really satisfied with the results, either in terms of dynamic range or the quality of the fit.

The voltage does fit nicely to the log of current, with IS=1.32µA and VT=27.1 mV. Given the quantization noise in the voltage measurement, these seem like a pretty good fit to theory. (click to embiggen)

One problem is that the serial variable resistor I used did not all really low currents.  I rewired it today so that I had a potentiometer providing the voltage, rather than a series variable resistor:

I also wrote a little Python program to merge different data files, so that I could combine files in which the resistance of R2 (for measuring the current) differed.

The resulting data fits the model well for over six decades (> 120dB):

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.

The measurements at the high-current end had to be redone with an external power supply for the Leonardo Arduino board (not just USB power), as the reference voltage for the A-to-D converter dipped as the load increased. There is a tiny effect still when using an external power supply, but only at the very highest current level, and it is buried in the noise.

At the low-current end, we can see the flattening of the curve from the “1+” term that is often omitted from the model.  The resolution in the voltage is poor there, but the current knee can be fairly accurately set by using a large value for R2.

I should probably characterize the base-emitter junction of a PNP and an NPN transistor this way also, for setting the appropriate resistances for the log amplifier in the loudness circuit.


Filed under: Circuits course, Data acquisition Tagged: Arduino, i-vs-v-plot, Schottky diode

Better I-V plot for Schottky diodes

Yesterday I posted a voltage-versus-current curve for a 1N5817 Schottky diode, to confirm the theoretical formula , where IS is the saturation current of the diode, but I wasn’t really satisfied with the results, either in terms of dynamic range or the quality of the fit.

The voltage does fit nicely to the log of current, with IS=1.32µA and VT=27.1 mV. Given the quantization noise in the voltage measurement, these seem like a pretty good fit to theory. (click to embiggen)

One problem is that the serial variable resistor I used did not all really low currents.  I rewired it today so that I had a potentiometer providing the voltage, rather than a series variable resistor:

I also wrote a little Python program to merge different data files, so that I could combine files in which the resistance of R2 (for measuring the current) differed.

The resulting data fits the model well for over six decades (> 120dB):

Fitting over a wide range of currents is more robust than fitting over the narrower range that I can get with just one value for R2.
There is quantization error still on the voltages, but the overlapping current ranges give good data for most of the range. VT is now 26.1mV and IS is 0.91µA.

The measurements at the high-current end had to be redone with an external power supply for the Leonardo Arduino board (not just USB power), as the reference voltage for the A-to-D converter dipped as the load increased. There is a tiny effect still when using an external power supply, but only at the very highest current level, and it is buried in the noise.

At the low-current end, we can see the flattening of the curve from the “1+” term that is often omitted from the model.  The resolution in the voltage is poor there, but the current knee can be fairly accurately set by using a large value for R2.

I should probably characterize the base-emitter junction of a PNP and an NPN transistor this way also, for setting the appropriate resistances for the log amplifier in the loudness circuit.


Filed under: Circuits course, Data acquisition Tagged: Arduino, i-vs-v-plot, Schottky diode

Using KL25Z for measuring salinity

Continuing the series of posts on measuring salinity with a couple of resistors and microprocessor

  1. Towards automatic measurement of conductivity of saline solution describes another possible freshman design project: a conductivity meter using the KL25Z board.
  2. More on automatic measurement of conductivity of saline solution looks at waveforms for square waves generated using PWM on the KL25Z board.  In this post I found that 100kHz square waves would work well.
  3. Still more on automatic measurement of conductivity of saline solution looks at waveforms for bursts of square waves generated by an Arduino board.  The bursts are limited to about 4kHz, but that may be good enough for a conductivity meter.

Today I started over on using the KL25Z board.  Since I wasn’t interested in precise frequencies, I didn’t use the PWM output this time, but used the same trick I used on the Arduino board: flipping the output bit, reading a sample, and repeating in a burst.

I record the sum of the differences between the high and low readings, and report the average at the end of each burst.  By using 40,000 cycles of warmup in each burst (discarded), then averaging over the next 10,000 cycles, I get a voltage reading that has a standard deviation of about 0.1mV on a reading of 2.843V, which is about 14–15 bits of accuracy.  The voltage reading is not constant, though, but drifts downward.

(click to embiggen) Voltage difference at undriven electrode as a function of time. The two sudden steps were probably the result of my jostling the table by putting down my teacup too hard.

I don’t have an explanation of the gradual drift in the voltage. I don’t think that this is a change in the salinity of the solution (which should be unchanged or increasing slowly due to evaporation). but a change in the characteristics of the electrodes. More likely, it is a change in the characteristics of the electrodes.  The sudden shifts when the table was jostled may be due to electrodes shifting their position in the cup or the release of a bubble.  Releasing a bubble should increase the surface area of the electrode and hence increase the conductivity and the voltage difference at the undriven electrode.  The gradual downward shift could be due to building up tiny hydrogen bubbles (too small to see) on the negative electrode.  The changes in voltage observed here are less than 0.1%, which is fairly respectable for a homebrew instrument.

Here is the (undocumented, throw-away) code that I wrote today to test out the ideas of an automatic salinity measurement system using a KL25Z:

#include "mbed.h"

DigitalInOut square_out(PTB0);   // PTB0=arduino A0
//PTB0, PTB1, PTD6, and PTD7 I/O have both high drive and normal drive capability selected by the associated PTx_PCRn[DSE] control bit.

AnalogIn IN(PTB1);  // PTB1=Arduino A1

Serial USB_io(USBTX, USBRX);  // defaults to 9600 8N1 (reset in main to 115200 baud)
Timer since_start;

#define WARMUP (40000)    // number of cycles of toggling output before collecting data
#define COLLECT (10000)   // number of cycles of data to sum for each output
#define Vdd (3.3)      // High voltage at output
int main()
{
    USB_io.baud(115200);
    USB_io.printf("\nusec\tvolts\nN\tN\n");

    //DEFAULT configuration of analog input
    ADC0->CFG1 = ADC_CFG1_ADLPC_MASK    // Low-Power Configuration
               | ADC_CFG1_ADIV(3)       // Clock Divide Select: (Input Clock)/8
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADICLK(1);    // Input Clock: (Bus Clock)/2

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // Long Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference

    ADC0->SC3 = ADC_SC3_AVGE_MASK       // Hardware Average Enable
                | ADC_SC3_AVGS(0);        // 4 Samples Averaged

    // FAST analog input
    ADC0->CFG1 =
                ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_ADICLK(0);    // Input Clock: (Bus Clock)

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // longest "long" Sample Time Select

//             | ADC_CFG2_ADLSTS(3);    // shortest "long" Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference
    ADC0->SC3 = 0;        // No hardware averaging

    // set PORTB pin 0 to high drive here
    PORTB->PCR[0]  |= PORT_PCR_DSE_MASK;

    since_start.start();
    while(1)
    {
         square_out.output();
         for (int i=0; i
         {
             square_out=1;
             wait_us(1);
             volatile uint16_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             volatile uint16_t fall_read=IN.read_u16();
        }
        int32_t sum=0;
        for (int i=0;i<COLLECT; i++)
        {
             square_out=1;
             wait_us(1);
             int32_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             sum += rise_read - IN.read_u16();
        }
        square_out.input(); // hiZ state when not driving pulses

        USB_io.printf("%10d\t%7.5f\n", since_start.read_us(), sum*(Vdd/COLLECT/(1<<16))); // scale output to volts
    }
 }

There is still a lot that needs to be done to make this a finished project, but I’ve convinced myself that it is doable as freshman design project, which is all I really needed to do.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z

Using KL25Z for measuring salinity

Continuing the series of posts on measuring salinity with a couple of resistors and microprocessor

  1. Towards automatic measurement of conductivity of saline solution describes another possible freshman design project: a conductivity meter using the KL25Z board.
  2. More on automatic measurement of conductivity of saline solution looks at waveforms for square waves generated using PWM on the KL25Z board.  In this post I found that 100kHz square waves would work well.
  3. Still more on automatic measurement of conductivity of saline solution looks at waveforms for bursts of square waves generated by an Arduino board.  The bursts are limited to about 4kHz, but that may be good enough for a conductivity meter.

Today I started over on using the KL25Z board.  Since I wasn’t interested in precise frequencies, I didn’t use the PWM output this time, but used the same trick I used on the Arduino board: flipping the output bit, reading a sample, and repeating in a burst.

I record the sum of the differences between the high and low readings, and report the average at the end of each burst.  By using 40,000 cycles of warmup in each burst (discarded), then averaging over the next 10,000 cycles, I get a voltage reading that has a standard deviation of about 0.1mV on a reading of 2.843V, which is about 14–15 bits of accuracy.  The voltage reading is not constant, though, but drifts downward.

(click to embiggen) Voltage difference at undriven electrode as a function of time. The two sudden steps were probably the result of my jostling the table by putting down my teacup too hard.

I don’t have an explanation of the gradual drift in the voltage. I don’t think that this is a change in the salinity of the solution (which should be unchanged or increasing slowly due to evaporation). but a change in the characteristics of the electrodes. More likely, it is a change in the characteristics of the electrodes.  The sudden shifts when the table was jostled may be due to electrodes shifting their position in the cup or the release of a bubble.  Releasing a bubble should increase the surface area of the electrode and hence increase the conductivity and the voltage difference at the undriven electrode.  The gradual downward shift could be due to building up tiny hydrogen bubbles (too small to see) on the negative electrode.  The changes in voltage observed here are less than 0.1%, which is fairly respectable for a homebrew instrument.

Here is the (undocumented, throw-away) code that I wrote today to test out the ideas of an automatic salinity measurement system using a KL25Z:

#include "mbed.h"

DigitalInOut square_out(PTB0);   // PTB0=arduino A0
//PTB0, PTB1, PTD6, and PTD7 I/O have both high drive and normal drive capability selected by the associated PTx_PCRn[DSE] control bit.

AnalogIn IN(PTB1);  // PTB1=Arduino A1

Serial USB_io(USBTX, USBRX);  // defaults to 9600 8N1 (reset in main to 115200 baud)
Timer since_start;

#define WARMUP (40000)    // number of cycles of toggling output before collecting data
#define COLLECT (10000)   // number of cycles of data to sum for each output
#define Vdd (3.3)      // High voltage at output
int main()
{
    USB_io.baud(115200);
    USB_io.printf("\nusec\tvolts\nN\tN\n");

    //DEFAULT configuration of analog input
    ADC0->CFG1 = ADC_CFG1_ADLPC_MASK    // Low-Power Configuration
               | ADC_CFG1_ADIV(3)       // Clock Divide Select: (Input Clock)/8
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADICLK(1);    // Input Clock: (Bus Clock)/2

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // Long Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference

    ADC0->SC3 = ADC_SC3_AVGE_MASK       // Hardware Average Enable
                | ADC_SC3_AVGS(0);        // 4 Samples Averaged

    // FAST analog input
    ADC0->CFG1 =
                ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_ADICLK(0);    // Input Clock: (Bus Clock)

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // longest "long" Sample Time Select

//             | ADC_CFG2_ADLSTS(3);    // shortest "long" Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference
    ADC0->SC3 = 0;        // No hardware averaging

    // set PORTB pin 0 to high drive here
    PORTB->PCR[0]  |= PORT_PCR_DSE_MASK;

    since_start.start();
    while(1)
    {
         square_out.output();
         for (int i=0; i
         {
             square_out=1;
             wait_us(1);
             volatile uint16_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             volatile uint16_t fall_read=IN.read_u16();
        }
        int32_t sum=0;
        for (int i=0;i<COLLECT; i++)
        {
             square_out=1;
             wait_us(1);
             int32_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             sum += rise_read - IN.read_u16();
        }
        square_out.input(); // hiZ state when not driving pulses

        USB_io.printf("%10d\t%7.5f\n", since_start.read_us(), sum*(Vdd/COLLECT/(1<<16))); // scale output to volts
    }
 }

There is still a lot that needs to be done to make this a finished project, but I’ve convinced myself that it is doable as freshman design project, which is all I really needed to do.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z

Still more on automatic measurement of conductivity of saline solution

In More on automatic measurement of conductivity of saline solution, I suggested using a simple voltage divider and a microcontroller to make conductivity measurements with polarizable electrodes:

Simplified circuit for conductivity tester.

I found that putting in a 100kHz square wave worked well:

At 100kHz, both the voltage waveforms (input and output) look like pretty good square waves.

I have not yet figured out a good way on the KL25Z to provide the 100kHz signal, sample the outputs at fixed points, and communicate the result out the USB port.  Using PWM for the output was handy for just generating the output (once I fixed mbed’s off-by-one bug in their pwmout_api.c file), but that makes connecting up the analog reads more difficult.  I think that I may be better off not using PWM, but using a timer interrupt to read the analog value, change the output, and do the subtraction.  It would be fairly easy to arrange that (though I’ll probably have to figure out all the registers for the sample-and-hold and the analog-to-digital converter, as the mbed AnalogIn routine is unlikely to have the settings I want to use).   The hard part remains the interface to the host computer, as mbed does not include a simple serial interface and serial monitor like the Arduino IDE. [Correction 2013 Dec 25: my son points out that the mbed development kit has a perfectly usable serial USB interface—I had overlooked the inheritance from "Stream", which has all the functions I thought were missing. I should be able to use the Arduino serial monitor with the Freedom KL25Z board, as long as the serial interface is set up right.]

Because I’m more familiar with the Arduino environment, and because I already have Arduino Data Logger code for the host end of the interface, I started by making a simple loop that toggles the output and reads the value after each change in output.  After repeating this several times (40 or 100), I take the last difference as the output and report that to the data logger.  I couldn’t get the frequency up where I really want it (100kHz), because the Arduino analog-to-digital converter is slow, but I was able to run at about 4kHz, which would be adequate.

Because there needs to be time for the serial communication, I did bursts of pulses with pauses between bursts.  The bursts were alternating as fast as the analog inputs were read for a fixed number of cycles, and the start of the bursts was controlled by the Arduino data logger software. Although the ends of the bursts looked the same on the oscilloscope, with the same peak-to-peak voltage, I got different readings from the Arduino depending on the spacing between the bursts. I’m not sure what is causing the discrepancy.

A difference at the beginnings of the bursts I would understand as the space between the bursts put a DC voltage across the electrodes which gradually charged them up, so that the first few pulses actually end up going outside the range of the ADC:

The bottom of the grid is 0v, and the first pulse goes up to 5.442v. The pulses are at about 4kHz, but the bursts start 50msec apart.

The differences at the ends of the bursts as I change the spacing between bursts are probably also due to the charging, though I don’t see that clearly on the oscilloscope. I really don’t like the idea of having a DC bias across the electrodes, as we get electrolysis, with hydrogen bubbles forming on the more negative electrode. No oxygen bubbles form, probably because any oxygen released is reacting with the stainless steel to form metal oxides. If I increase the voltage and current, I get a lot of hydrogen bubbles on the negative electrode, some rusty looking precipitate coming off the positive electrode (probably an iron oxide), and a white coating building up on the positive electrode (probably a chromium oxide).

By putting a 4.7µF capacitor between the Arduino output and the electrode, I can reduce DC bias on the electrodes and get a more consistent signal from the Arduino, almost independent of the spacing between the bursts:

By using a 25msec spacing between the beginnings of bursts, I can get both the end of the burst and the beginning of the burst on the oscilloscope at once.
Using a 4.7µF capacitor between the square wave output and the electrodes results in sharp peaks across the resistor, but a more consistent reading from the Arduino ADC.

The voltage across the electrodes still does not average to 0v, as the pair of resistors provides a bias voltage halfway between the rails, but the pulse does not really swing rail to rail, but from 0.28v to 4.28v.  I think that the low-pass filter for setting the bias voltage that I suggested in More on automatic measurement of conductivity of saline solution may be a good idea after all, to make sure that there is no residual DC bias.

I can use the differential inputs of the Bitscope DP01 to look at the voltage across the electrodes and across the resistor to get voltage and current traces for the electrodes:

The central horizontal line is 0V for both traces here. The green trace is the voltage at the undriven electrode (@ 2v/division) and so corresponds to the current, and the yellow trace is the voltage between the electrodes (@0.2v/division).

Note that the voltage on the undriven electrode does run a little below 0V, outside the range of the Arduino ADC.  The voltage ratio of 0.248v/4.16v, together with the 100Ω Thévenin equivalent resistance results in a 5.47Ω resistance between the electrodes.  (Note: this is no longer a 1M NaCl solution—there has been evaporation, plus contamination from iron oxides, and the electrodes are not covered to the depth defined by the plastic spacer.)

I don’t know whether the conductivity meter is a good project for the freshman design seminar or not—I don’t expect the students to have the circuit skills or the programming skills to be able to do a design like this without a lot of coaching.  Even figuring out that they need to eliminate DC bias to eliminate electrolysis may be too much for them, though I do expect all to have had at least high-school chemistry. It is probably worth doing a demo of putting a large current through electrodes in salt solution, to show both the hydrogen bubbles and the formation of the oxides.  I could probably coach freshmen through the design, if they were interested in doing it, so I’ll leave it on the feasible list.

The square-wave analysis is not really suitable for a circuits course, so I think I’ll stick with sine-wave excitation for that course.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z, voltage divider

Still more on automatic measurement of conductivity of saline solution

In More on automatic measurement of conductivity of saline solution, I suggested using a simple voltage divider and a microcontroller to make conductivity measurements with polarizable electrodes:

Simplified circuit for conductivity tester.

I found that putting in a 100kHz square wave worked well:

At 100kHz, both the voltage waveforms (input and output) look like pretty good square waves.

I have not yet figured out a good way on the KL25Z to provide the 100kHz signal, sample the outputs at fixed points, and communicate the result out the USB port.  Using PWM for the output was handy for just generating the output (once I fixed mbed’s off-by-one bug in their pwmout_api.c file), but that makes connecting up the analog reads more difficult.  I think that I may be better off not using PWM, but using a timer interrupt to read the analog value, change the output, and do the subtraction.  It would be fairly easy to arrange that (though I’ll probably have to figure out all the registers for the sample-and-hold and the analog-to-digital converter, as the mbed AnalogIn routine is unlikely to have the settings I want to use).   The hard part remains the interface to the host computer, as mbed does not include a simple serial interface and serial monitor like the Arduino IDE. [Correction 2013 Dec 25: my son points out that the mbed development kit has a perfectly usable serial USB interface—I had overlooked the inheritance from "Stream", which has all the functions I thought were missing. I should be able to use the Arduino serial monitor with the Freedom KL25Z board, as long as the serial interface is set up right.]

Because I’m more familiar with the Arduino environment, and because I already have Arduino Data Logger code for the host end of the interface, I started by making a simple loop that toggles the output and reads the value after each change in output.  After repeating this several times (40 or 100), I take the last difference as the output and report that to the data logger.  I couldn’t get the frequency up where I really want it (100kHz), because the Arduino analog-to-digital converter is slow, but I was able to run at about 4kHz, which would be adequate.

Because there needs to be time for the serial communication, I did bursts of pulses with pauses between bursts.  The bursts were alternating as fast as the analog inputs were read for a fixed number of cycles, and the start of the bursts was controlled by the Arduino data logger software. Although the ends of the bursts looked the same on the oscilloscope, with the same peak-to-peak voltage, I got different readings from the Arduino depending on the spacing between the bursts. I’m not sure what is causing the discrepancy.

A difference at the beginnings of the bursts I would understand as the space between the bursts put a DC voltage across the electrodes which gradually charged them up, so that the first few pulses actually end up going outside the range of the ADC:

The bottom of the grid is 0v, and the first pulse goes up to 5.442v. The pulses are at about 4kHz, but the bursts start 50msec apart.

The differences at the ends of the bursts as I change the spacing between bursts are probably also due to the charging, though I don’t see that clearly on the oscilloscope. I really don’t like the idea of having a DC bias across the electrodes, as we get electrolysis, with hydrogen bubbles forming on the more negative electrode. No oxygen bubbles form, probably because any oxygen released is reacting with the stainless steel to form metal oxides. If I increase the voltage and current, I get a lot of hydrogen bubbles on the negative electrode, some rusty looking precipitate coming off the positive electrode (probably an iron oxide), and a white coating building up on the positive electrode (probably a chromium oxide).

By putting a 4.7µF capacitor between the Arduino output and the electrode, I can reduce DC bias on the electrodes and get a more consistent signal from the Arduino, almost independent of the spacing between the bursts:

By using a 25msec spacing between the beginnings of bursts, I can get both the end of the burst and the beginning of the burst on the oscilloscope at once.
Using a 4.7µF capacitor between the square wave output and the electrodes results in sharp peaks across the resistor, but a more consistent reading from the Arduino ADC.

The voltage across the electrodes still does not average to 0v, as the pair of resistors provides a bias voltage halfway between the rails, but the pulse does not really swing rail to rail, but from 0.28v to 4.28v.  I think that the low-pass filter for setting the bias voltage that I suggested in More on automatic measurement of conductivity of saline solution may be a good idea after all, to make sure that there is no residual DC bias.

I can use the differential inputs of the Bitscope DP01 to look at the voltage across the electrodes and across the resistor to get voltage and current traces for the electrodes:

The central horizontal line is 0V for both traces here. The green trace is the voltage at the undriven electrode (@ 2v/division) and so corresponds to the current, and the yellow trace is the voltage between the electrodes (@0.2v/division).

Note that the voltage on the undriven electrode does run a little below 0V, outside the range of the Arduino ADC.  The voltage ratio of 0.248v/4.16v, together with the 100Ω Thévenin equivalent resistance results in a 5.47Ω resistance between the electrodes.  (Note: this is no longer a 1M NaCl solution—there has been evaporation, plus contamination from iron oxides, and the electrodes are not covered to the depth defined by the plastic spacer.)

I don’t know whether the conductivity meter is a good project for the freshman design seminar or not—I don’t expect the students to have the circuit skills or the programming skills to be able to do a design like this without a lot of coaching.  Even figuring out that they need to eliminate DC bias to eliminate electrolysis may be too much for them, though I do expect all to have had at least high-school chemistry. It is probably worth doing a demo of putting a large current through electrodes in salt solution, to show both the hydrogen bubbles and the formation of the oxides.  I could probably coach freshmen through the design, if they were interested in doing it, so I’ll leave it on the feasible list.

The square-wave analysis is not really suitable for a circuits course, so I think I’ll stick with sine-wave excitation for that course.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z, voltage divider