Posts with «data acquisition» label

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

Digital filters for pulse monitor

In Optical pulse monitor with little electronics, I talked a bit about an optical pulse monitor using the Arduino and just 4 components (2 resistors, an IR emitter, and a phototransistor).  Yesterday, I had gotten as far as getting good values for resistors, doing synchronous decoding, and using a very simple low-pass IIR filter to clean up the noise.  The final result still had problems with the baseline shifting (probably due to slight movements of my finger in the sensor):

(click to embiggen) Yesterday’s plot with digital low-pass filtering, using y(t) = (x(t) + 7 y(t-1) )/8.  There is not much noise, but the baseline wobbles up and down a lot, making the signal hard to process automatically.

Today I decided to brush off my digital filter knowledge, which I haven’t used much lately, and see if I could design a filter using only small integer arithmetic on the Arduino, to clean up the signal more. I decided to use a sampling rate fs = 30Hz on the Arduino, to avoid getting any beating due to 60Hz pickup (not that I’ve seen much with my current setup). The 30Hz choice was made because I do two measurements (IR on and IR off) for each sample, so my actual measurements are at 60Hz, and should be in the same place in any noise waveform that is picked up. (Europeans with 50Hz line frequency would want to use 25Hz as their sampling frequency.)

With the 680kΩ resistor that I selected yesterday, the 30Hz sampling leaves plenty of time for the signal to charge and discharge:

The grid line in the center is at 3v. The green trace is the signal to on the positive side of the IR LED, so the LED is on when the trace is low (with 32mA current through the pullup resistor). The yellow trace is the voltage at the Arduino input pin: high when light is visible, low when it is dark. This recording was made with my middle finger between the LED and the phototransistor.

I decided I wanted to replace the low-pass filter with a passband filter, centered near 1Hz (60 beats per minute), but with a range of about 0.4Hz (24 bpm) to 4Hz (240bpm). I don’t need the passband to be particularly flat, so I decided to go with a simple 2-pole, 2-zero filter (called a biquad filter). This filter has the transfer function

To get the gain of the filter at a frequency f, you just compute , where .  Note that the z values that correspond to sinusoids are along the unit circle, from DC at up to the Nyquist frequency at .

The filter is implemented as a simple recurrence relation between the input x and the output y:

This is known as the “direct” implementation.  It takes a bit more memory than the “canonical” implementation, but has some nice properties when used with small-word arithmetic—the intermediate values never get any further from 0 than the output and input values, so there is no overflow to worry about in intermediate computations.

I tried using an online web tool to design the filter http://www-users.cs.york.ac.uk/~fisher/mkfilter/, and I got some results but not everything on the page is working.  One can’t very well complain to Tony Fisher about the maintenance, since he died in 2000. I tried using the tool at http://digitalfilter.com/enindex.html to look at filter gain, but it has an awkward x-axis (linear instead of logarithmic frequency) and was a bit annoying to use.  So I looked at results from Tony Fisher’s program, then used my own gnuplot script to look at the response for filter parameters I was interested in.

The filter program gave me one obvious result (that I should not have needed a program to realize): the two zeros need to be at DC and the Nyquist frequency—that is at ±1.  That means that the numerator of the transfer function is just , and b0=1, b1=0, and b2=–1.  The other two parameters it gave me were a2=0.4327386423 and a1=–1.3802466192.  Of course, I don’t want to use floating-point arithmetic, but small integer arithmetic, so that the only division I do is by powers of 2 (which the compiler turns into a quick shift operation).

I somewhat arbitrarily selected 32 as my power of 2 to divide by, so that my transfer function is now

and my recurrence relation is

with A1 and A2 restricted to be integers.  Rounding the numbers from Fisher’s program suggested A1=-44 and A2=14, but that centered the filter at a bit higher frequency than I liked, so I tweaked the parameters and drew plots to see what the gain function looked like.  I made one serious mistake initially—I neglected to check that the two poles were both inside the unit circle (they were real-valued poles, so the check was just applying the quadratic formula).  My first design (not the one from Fisher’s program) had one pole outside the unit circle—it looked fine on the plot, but when I implemented it, the values grew until the word size was exceeded, then oscillated all over the place.  When I realized what was wrong, I checked the stability criterion and changed the A2 value to make the pole be inside the unit circle.

I eventually ended up with A1=-48 and A2=17, which centered the filter at 1, but did not have as high an upper frequency as I had originally thought I wanted:

(click to embiggen) The gain of the filter that I ended up implementing has -3dB points at about 0.43 and 2.15 Hz.

Here is the gnuplot script I used to generate the plot—it is not fully automatic (the xtics, for example, are manually set). Click it to expand.

fs = 30	# sampling frequency
A0=32.  # multiplier (use power of 2)
b=16.

A1=-(A0+b)
A2=b+1

peak = fs/A0	# approx frequency of peak of filter

set title sprintf("Design of biquad filter, fs=%3g Hz",fs)

set key bottom center
set ylabel "gain [dB]"
unset logscale y
set yrange [-20:30]

set xlabel "frequency [Hz]"
set logscale x
set xrange [0.01:0.5*fs]

set xtics add (0.43, 2.15)
set grid xtics

j=sqrt(-1)
biquad(zinv,b0,b1,b2,a0,a1,a2) = (b0+zinv*(b1+zinv*b2))/(a0+zinv*(a1+zinv*a2))
gain(f,b0,b1,b2,a0,a1,a2) = abs( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2))
phase(f,b0,b1,b2,a0,a1,a2) = imag(log( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2)))

plot 20*log(gain(x,A0,0,-A0,  A0,A1,A2)) \
		title sprintf("%.0f (1-z^-2)/(%.0f+ %.0f z^-1 + %.0f z^-2)", \
			A0, A0, A1, A2), \
	20*log(gain(peak,A0,0,-A0,  A0,A1,A2))-3 title "approx -3dB"

I wrote a simple Arduino program to sample the phototransistor every 1/60th of a second, alternating between IR off and IR on. After each IR-on reading, I output the time, the difference between on and off readings, and the filtered difference. (click on the code box to view it)

#include "TimerOne.h"

#define rLED 3
#define irLED 5

// #define CANONICAL   // use canonical, rather than direct implementation of IIR filter
// Direct implementation seems to avoid overflow better.
// There is probably still a bug in the canonical implementation, as it is quite unstable.

#define fs (30) // sampling frequency in Hz
#define half_period (500000L/fs)  // half the period in usec

#define multiplier  32      // power of 2 near fs
#define a1  (-48)           // -(multiplier+k)
#define a2  (17)            // k+1

volatile uint8_t first_tick;    // Is this the first tick after setup?
void setup(void)
{
    Serial.begin(115200);
//    pinMode(rLED,OUTPUT);
    pinMode(irLED,OUTPUT);
//    digitalWrite(rLED,1);  // Turn RED LED off
    digitalWrite(irLED,1); // Turn IR LED off

    Serial.print("# bandpass IIR filter\n# fs=");
    Serial.print(fs);
    Serial.print(" Hz, period=");
    Serial.print(2*half_period);
    Serial.print(" usec\n#  H(z) = ");
    Serial.print(multiplier);
    Serial.print("(1-z^-2)/(");
    Serial.print(multiplier);
    Serial.print(" + ");
    Serial.print(a1);
    Serial.print("z^-1 + ");
    Serial.print(a2);
    Serial.println("z^-2)");
#ifdef CANONICAL
    Serial.println("# using canonical implementation");
#else
    Serial.println("# using direct implementation");
#endif
    Serial.println("#  microsec raw   filtered");

    first_tick=1;
    Timer1.initialize(half_period);
    Timer1.attachInterrupt(half_period_tick,half_period);
}

#ifdef CANONICAL
// for canonical implementation
 volatile int32_t w_0, w_1, w_2;
#else
// For direct implementation
 volatile int32_t x_1,x_2, y_0,y_1,y_2;
#endif

void loop()
{
}

volatile uint8_t IR_is_on=0;    // current state of IR LED
volatile uint16_t IR_off;       // reading when IR is off (stored until next tick)

void half_period_tick(void)
{
    uint32_t timestamp=micros();

    uint16_t IR_read;
    IR_read = analogRead(0);
    if (!IR_is_on)
    {   IR_off=IR_read;
        digitalWrite(irLED,0); // Turn IR LED on
        IR_is_on = 1;
        return;
    }

    digitalWrite(irLED,1); // Turn IR LED off
    IR_is_on = 0;

    Serial.print(timestamp);
    Serial.print(" ");

    int16_t x_0 = IR_read-IR_off;
    Serial.print(x_0);
    Serial.print(" ");

 #ifdef CANONICAL
    if (first_tick)
    {  // I'm not sure how to initialize w for the first tick
       w_2 = w_1 = multiplier*x_0/ (1+a1+a2);
       first_tick = 0;
    }
 #else
    if (first_tick)
    {   x_2 = x_1 = x_0;
        first_tick = 0;
    }
#endif

#ifdef CANONICAL
    w_0 = multiplier*x_0 - a1*w_1 -a2*w_2;
    int32_t y_0 = w_0 - w_2;
    Serial.println(y_0);
    w_2=w_1;
    w_1=w_0;
#else
     y_0 = multiplier*(x_0-x_2) - a1*y_1 -a2*y_2;
     Serial.println(y_0);
     y_0 /= multiplier;
     x_2 = x_1;
     x_1 = x_0;
     y_2 = y_1;
     y_1 = y_0;
#endif
}

Here are a couple of examples of the input and output of the filtering:

(click to embiggen) The input signals here are fairly clean, but different runs often get quite different amounts of light through the finger, depending on which finger is used and the alignment with the phototransistor. Note that the DC offset shifts over the course of each run.

(click to embiggen) After filtering the DC offset and the baseline shift are gone. The two very different input sequences now have almost the same range. There is a large, clean downward spike at the beginning of each pulse.

Overall, I’m pretty happy with the results of doing digital filtering here. Even a crude 2-zero, 2-pole filter using just integer arithmetic does an excellent job of cleaning up the signal.


Filed under: Circuits course, Data acquisition, freshman design seminar Tagged: Arduino, biquad filter, digital filter, IIR filter, IR emitter, LED, phototransistor, pulse, pulse monitor, pulse oximeter

Digital filters for pulse monitor

In Optical pulse monitor with little electronics, I talked a bit about an optical pulse monitor using the Arduino and just 4 components (2 resistors, an IR emitter, and a phototransistor).  Yesterday, I had gotten as far as getting good values for resistors, doing synchronous decoding, and using a very simple low-pass IIR filter to clean up the noise.  The final result still had problems with the baseline shifting (probably due to slight movements of my finger in the sensor):

(click to embiggen) Yesterday’s plot with digital low-pass filtering, using y(t) = (x(t) + 7 y(t-1) )/8.  There is not much noise, but the baseline wobbles up and down a lot, making the signal hard to process automatically.

Today I decided to brush off my digital filter knowledge, which I haven’t used much lately, and see if I could design a filter using only small integer arithmetic on the Arduino, to clean up the signal more. I decided to use a sampling rate fs = 30Hz on the Arduino, to avoid getting any beating due to 60Hz pickup (not that I’ve seen much with my current setup). The 30Hz choice was made because I do two measurements (IR on and IR off) for each sample, so my actual measurements are at 60Hz, and should be in the same place in any noise waveform that is picked up. (Europeans with 50Hz line frequency would want to use 25Hz as their sampling frequency.)

With the 680kΩ resistor that I selected yesterday, the 30Hz sampling leaves plenty of time for the signal to charge and discharge:

The grid line in the center is at 3v. The green trace is the signal to on the positive side of the IR LED, so the LED is on when the trace is low (with 32mA current through the pullup resistor). The yellow trace is the voltage at the Arduino input pin: high when light is visible, low when it is dark. This recording was made with my middle finger between the LED and the phototransistor.

I decided I wanted to replace the low-pass filter with a passband filter, centered near 1Hz (60 beats per minute), but with a range of about 0.4Hz (24 bpm) to 4Hz (240bpm). I don’t need the passband to be particularly flat, so I decided to go with a simple 2-pole, 2-zero filter (called a biquad filter). This filter has the transfer function

To get the gain of the filter at a frequency f, you just compute , where .  Note that the z values that correspond to sinusoids are along the unit circle, from DC at up to the Nyquist frequency at .

The filter is implemented as a simple recurrence relation between the input x and the output y:

This is known as the “direct” implementation.  It takes a bit more memory than the “canonical” implementation, but has some nice properties when used with small-word arithmetic—the intermediate values never get any further from 0 than the output and input values, so there is no overflow to worry about in intermediate computations.

I tried using an online web tool to design the filter http://www-users.cs.york.ac.uk/~fisher/mkfilter/, and I got some results but not everything on the page is working.  One can’t very well complain to Tony Fisher about the maintenance, since he died in 2000. I tried using the tool at http://digitalfilter.com/enindex.html to look at filter gain, but it has an awkward x-axis (linear instead of logarithmic frequency) and was a bit annoying to use.  So I looked at results from Tony Fisher’s program, then used my own gnuplot script to look at the response for filter parameters I was interested in.

The filter program gave me one obvious result (that I should not have needed a program to realize): the two zeros need to be at DC and the Nyquist frequency—that is at ±1.  That means that the numerator of the transfer function is just , and b0=1, b1=0, and b2=–1.  The other two parameters it gave me were a2=0.4327386423 and a1=–1.3802466192.  Of course, I don’t want to use floating-point arithmetic, but small integer arithmetic, so that the only division I do is by powers of 2 (which the compiler turns into a quick shift operation).

I somewhat arbitrarily selected 32 as my power of 2 to divide by, so that my transfer function is now

and my recurrence relation is

with A1 and A2 restricted to be integers.  Rounding the numbers from Fisher’s program suggested A1=-44 and A2=14, but that centered the filter at a bit higher frequency than I liked, so I tweaked the parameters and drew plots to see what the gain function looked like.  I made one serious mistake initially—I neglected to check that the two poles were both inside the unit circle (they were real-valued poles, so the check was just applying the quadratic formula).  My first design (not the one from Fisher’s program) had one pole outside the unit circle—it looked fine on the plot, but when I implemented it, the values grew until the word size was exceeded, then oscillated all over the place.  When I realized what was wrong, I checked the stability criterion and changed the A2 value to make the pole be inside the unit circle.

I eventually ended up with A1=-48 and A2=17, which centered the filter at 1, but did not have as high an upper frequency as I had originally thought I wanted:

(click to embiggen) The gain of the filter that I ended up implementing has -3dB points at about 0.43 and 2.15 Hz.

Here is the gnuplot script I used to generate the plot—it is not fully automatic (the xtics, for example, are manually set). Click it to expand.

fs = 30	# sampling frequency
A0=32.  # multiplier (use power of 2)
b=16.

A1=-(A0+b)
A2=b+1

peak = fs/A0	# approx frequency of peak of filter

set title sprintf("Design of biquad filter, fs=%3g Hz",fs)

set key bottom center
set ylabel "gain [dB]"
unset logscale y
set yrange [-20:30]

set xlabel "frequency [Hz]"
set logscale x
set xrange [0.01:0.5*fs]

set xtics add (0.43, 2.15)
set grid xtics

j=sqrt(-1)
biquad(zinv,b0,b1,b2,a0,a1,a2) = (b0+zinv*(b1+zinv*b2))/(a0+zinv*(a1+zinv*a2))
gain(f,b0,b1,b2,a0,a1,a2) = abs( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2))
phase(f,b0,b1,b2,a0,a1,a2) = imag(log( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2)))

plot 20*log(gain(x,A0,0,-A0,  A0,A1,A2)) \
		title sprintf("%.0f (1-z^-2)/(%.0f+ %.0f z^-1 + %.0f z^-2)", \
			A0, A0, A1, A2), \
	20*log(gain(peak,A0,0,-A0,  A0,A1,A2))-3 title "approx -3dB"

I wrote a simple Arduino program to sample the phototransistor every 1/60th of a second, alternating between IR off and IR on. After each IR-on reading, I output the time, the difference between on and off readings, and the filtered difference. (click on the code box to view it)

#include "TimerOne.h"

#define rLED 3
#define irLED 5

// #define CANONICAL   // use canonical, rather than direct implementation of IIR filter
// Direct implementation seems to avoid overflow better.
// There is probably still a bug in the canonical implementation, as it is quite unstable.

#define fs (30) // sampling frequency in Hz
#define half_period (500000L/fs)  // half the period in usec

#define multiplier  32      // power of 2 near fs
#define a1  (-48)           // -(multiplier+k)
#define a2  (17)            // k+1

volatile uint8_t first_tick;    // Is this the first tick after setup?
void setup(void)
{
    Serial.begin(115200);
//    pinMode(rLED,OUTPUT);
    pinMode(irLED,OUTPUT);
//    digitalWrite(rLED,1);  // Turn RED LED off
    digitalWrite(irLED,1); // Turn IR LED off

    Serial.print("# bandpass IIR filter\n# fs=");
    Serial.print(fs);
    Serial.print(" Hz, period=");
    Serial.print(2*half_period);
    Serial.print(" usec\n#  H(z) = ");
    Serial.print(multiplier);
    Serial.print("(1-z^-2)/(");
    Serial.print(multiplier);
    Serial.print(" + ");
    Serial.print(a1);
    Serial.print("z^-1 + ");
    Serial.print(a2);
    Serial.println("z^-2)");
#ifdef CANONICAL
    Serial.println("# using canonical implementation");
#else
    Serial.println("# using direct implementation");
#endif
    Serial.println("#  microsec raw   filtered");

    first_tick=1;
    Timer1.initialize(half_period);
    Timer1.attachInterrupt(half_period_tick,half_period);
}

#ifdef CANONICAL
// for canonical implementation
 volatile int32_t w_0, w_1, w_2;
#else
// For direct implementation
 volatile int32_t x_1,x_2, y_0,y_1,y_2;
#endif

void loop()
{
}

volatile uint8_t IR_is_on=0;    // current state of IR LED
volatile uint16_t IR_off;       // reading when IR is off (stored until next tick)

void half_period_tick(void)
{
    uint32_t timestamp=micros();

    uint16_t IR_read;
    IR_read = analogRead(0);
    if (!IR_is_on)
    {   IR_off=IR_read;
        digitalWrite(irLED,0); // Turn IR LED on
        IR_is_on = 1;
        return;
    }

    digitalWrite(irLED,1); // Turn IR LED off
    IR_is_on = 0;

    Serial.print(timestamp);
    Serial.print(" ");

    int16_t x_0 = IR_read-IR_off;
    Serial.print(x_0);
    Serial.print(" ");

 #ifdef CANONICAL
    if (first_tick)
    {  // I'm not sure how to initialize w for the first tick
       w_2 = w_1 = multiplier*x_0/ (1+a1+a2);
       first_tick = 0;
    }
 #else
    if (first_tick)
    {   x_2 = x_1 = x_0;
        first_tick = 0;
    }
#endif

#ifdef CANONICAL
    w_0 = multiplier*x_0 - a1*w_1 -a2*w_2;
    int32_t y_0 = w_0 - w_2;
    Serial.println(y_0);
    w_2=w_1;
    w_1=w_0;
#else
     y_0 = multiplier*(x_0-x_2) - a1*y_1 -a2*y_2;
     Serial.println(y_0);
     y_0 /= multiplier;
     x_2 = x_1;
     x_1 = x_0;
     y_2 = y_1;
     y_1 = y_0;
#endif
}

Here are a couple of examples of the input and output of the filtering:

(click to embiggen) The input signals here are fairly clean, but different runs often get quite different amounts of light through the finger, depending on which finger is used and the alignment with the phototransistor. Note that the DC offset shifts over the course of each run.

(click to embiggen) After filtering the DC offset and the baseline shift are gone. The two very different input sequences now have almost the same range. There is a large, clean downward spike at the beginning of each pulse.

Overall, I’m pretty happy with the results of doing digital filtering here. Even a crude 2-zero, 2-pole filter using just integer arithmetic does an excellent job of cleaning up the signal.


Filed under: Circuits course, Data acquisition, freshman design seminar Tagged: Arduino, biquad filter, digital filter, IIR filter, IR emitter, LED, phototransistor, pulse, pulse monitor, pulse oximeter

Optical pulse monitor with little electronics

In yesterday’s blog post, I talked mainly about what my son did with his time yesterday, to mention the small amount of debugging help I gave him.  Today I’ll post about what I did with most of my time yesterday.

This year, I am hoping to lead a 2-unit freshman design seminar for bioengineering students.  (Note: I did not say “teach” here, as I’m envisioning more of a mentoring role than a specific series of exercises.)  One thing I’m doing is trying to come up with design projects that freshmen with essentially no engineering skills can do as a team.  They may have to learn something new (I certainly hope they do!), but they should only spend a total of 60 hours on the course, including class time.  Since I want to spend some of class time on lab tours, lab safety, using the library resources, and how to work in a group effectively, there is not a lot of time left for the actual design and implementation.

One of the things I found very valuable in designing the Applied Circuits course was doing all the design labs myself, sometimes several times, in order to tweak the specs and anticipate where the students would have difficulty.  I expect to do some redesign of a couple of the circuits labs this year, but that course is scheduled for Spring (and finally got official approval this week), while the (not yet approved) freshman seminar is scheduled for Winter.  So I’m now experimenting with projects that I think may be suitable for the freshman design seminar.

These students cannot individually be expected to know anything useful, high school in California being what it is.  As a group, though, I think I can expect a fair amount of knowledge of biology, chemistry, and physics, with perhaps a sprinkling of math and computer programming.  I can’t expect any electronics knowledge, and we won’t have access to a machine shop—we may get permission for the students to use a laser cutter under supervision.  We can probably get some space in an electronics lab, but maybe not in a bio lab (the dean took away the department’s only teaching lab, with a “promise” to build a bigger one—but it is unlikely to be available for the freshmen by Winter quarter—I miss our first dean of engineering, as we seem to have had a series of incompetent deans since then).

So I’m looking for projects that can essentially be built at home with minimal tools and skills, but that are interesting enough to excite students to continue to higher levels in the program.  And I want them to be design projects, not kit-building or cookbook projects, with multiple possible solutions.

So far, there have been a couple of ideas suggested, both involving a small amount of electronics and some mechanical design:

  • An optical growth meter for continuously monitoring a liquid culture of bacteria or yeast. The electronics here is just a light source (LED or laser diode with current-limiting resistor), a phototransistor,  a current-to-voltage converter for the phototransistor (one resistor), and a data logger (like the Arduino Data Logger we use for the circuits course).  The hard part is coming up with a good way to get uniform sampling of the liquid culture while it is in an incubator on a shaker table.  There are lots of possible solutions: mounting stuff around flasks, immersed sensors, bending glass tubing so that the swirling culture is pumped through the tubes, external peristaltic pumps, … .  Design challenges include how the parts of the apparatus that touch the culture will be sterilized, how to keep things from falling apart when they are shaken for a couple of days, and so forth.   I’ve not even started trying to do a design here, though I probably should, as the mechanical design is almost all unfamiliar to me, and I’d be a good example of the cluelessness that the students would bring to the project.
  • An optical pulse sensor or pulse oximeter.  This is the project I decided to work on yesterday. The goal is to shine light through a finger and get a good pulse signal.  (I tried this last summer, making a very uncomfortable ear clip and doing a little testing before rejecting the project for the circuits course.)  If I can get good pulse signals from both red and IR light sources, I should be able to take differences or ratios and get an output proportional to blood oxygenation.

I decided yesterday to try to build a pulse monitor with almost no electronics.  In particular, I wanted to try building without an op amp or other amplifier, feeding the phototransistor signal directly into an Arduino analog in.  (I may switch to using the KL25Z for this project, as the higher resolution on the analog-to-digital converter means I could use smaller signals without amplification.)

A phototransistor is essentially a light-to-current converter.  The current through the phototransistor is essentially linear in the amount of light, over a pretty wide range. The Arduino analog inputs are voltage sensors, so we need to convert the current to a voltage.  The simplest way to do this is to put a series resistor to ground, and measure the voltage across the series resistor.  The voltage we see is then the current times the resistance.  Sizing the resistor is a design task—how big a current do we get with the intensity of light through the finger, and how much voltage do we need. The voltage needed can be estimated from the resolution of the analog-to-digital converter, but the amount of light is best measured empirically.

One problem that the pulse monitor faces is huge variations in ambient light.  Ideally the phototransistor gets light only from LED light shining through the finger, but that is a bit hard to set up while breadboarding.  Distinguishing the ambient light from the light through the finger can be difficult. Yesterday, I tried to reduce that problem by using “synchronous decoding”.  That is, I turned the LED on and off, and measured the difference between the phototransistor current with the LED on and with the LED off.  Using the Arduino to control the LED as well as to read the voltage is fairly easy—these are the sorts of tasks that are starter projects on the Arduino, so should be within the capabilities of the freshmen (with some learning on their part).

I also looked at the phototransistor output with my BitScope oscilloscope, so that I could see the waveform that the Arduino was sampling two points from.  Here is an example waveform:

The x-axis is 20ms/division, and the y-axis 1v/division, with the center line at 2v.
I put in a 50% duty cycle (20ms on, 20ms off).  The IR light is shining through my index finger.

For the above trace, I used a 680kΩ pulldown resistor to convert the current to voltage. I played a lot with different resistors yesterday, to get a feel for the tradeoffs.  Such a large resistor provides a large voltage swing for a small change in current, but the parasitic capacitance makes for rather slow RC charge/discharge curves.  Using larger resistors does not result in larger swings (unless the frequency of the input is reduced), because the RC time constant gets too large and the slowly changing signal does not have time to make a full swing.  I tried, as an experiment, adding a unity-gain buffer, so that the BitScope and Arduino inputs would not be loading the phototransistor.  This did not make much difference, so most of the parasitic capacitance is probably in the phototransistor itself.  One can get faster response for a fixed change in light only by decreasing the voltage swing, which would then require amplification to get a big enough signal to be read by the Arduino.  (It may be that the extra 6 bits of resolution on the KL25Z board would allow a resistor as low as 20kΩ and much faster response.)

Note that ambient light results in a DC shift of the waveform without a change in shape, until it gets bright enough that the current is more than 5v/680kΩ (about 7µA), at which point the signal gets clipped.  Unfortunately, ordinary room lighting is enough to saturate the sensor with this large a series resistor.  I was able to get fairly consistent readings by using the clothespin ear clip I made last summer, clamped open to make it the right size for my finger.  I did even better when I put the clip and my hand into a camera bag that kept out most of the ambient light.  Clearly, mechanical design for eliminating ambient light will be a big part of this design.

One might think that the 2v signal seen on the BitScope is easily big enough for pulse detection, but remember that this is not the signal we are interested in.  The peak-to-peak voltage is proportional to how transparent the finger is—we are interested in the variation of that amplitude with blood flow.  Here is an example plot of the sort of signal we are looking at:

(click to embiggen) The pulse here is quite visible, but is only about a 15–30 count change in the 300-count amplitude signal. Noise from discretization (and other sources) makes the signal hard to pick out auotmatically.  This signal was recorded with the Arduino data logger, but only after I had modified the data logger code to turn the IR emitter on and off and report differences in the readings rather than the readings themselves. Note the sharp downward transition (increased opacity due to more blood) at the beginning of each pulse.

To get a bigger, cleaner signal, I decided to do some very crude low-pass filtering on the Arduino. I used the simplest of infinite-impulse response (IIR) filters: . Because division is very slow on the Arduino, I limited myself to simple shifts for division: a= 1/2, 1/4, or 1/8. To avoid losing even more precision, I actually output then divided by 8 to get Y(t). I also used a 40msec sampling period, with the IR emitter on for 20ms, then off for 20msec (the waveform shown in the oscilloscope trace above).

(click to embiggen) With digital low-pass filtering, the pulse signal is much cleaner, but the sharp downward transition at the start of each pulse has been rounded off by the filter. This data was not captured with the Arduino Data Logger, but by cutting and pasting from the Arduino serial monitor, which involves simpler (hence more feasible for freshmen) programming of the Arduino.

I now have a very clean pulse signal, using just the Arduino, an IR emitter, a phototransistor, and two resistors. There is still a huge offset, as the signal is 200 counts out of 4600, and the offset fluctuates slowly.  To get a really good signal, I’d want to do a bandpass filter that passes 0.3Hz to 3Hz (20bpm–200bpm), but designing that digital filter would be beyond the scope of a freshman design seminar.  Even the simple IIR filter is pushing a bit here.

I’m not sure how to go from here to the pulse oximeter (using both an IR and a red LED) without fancy digital filtering.  Here is the circuit so far:

Although the 120Ω resistor allows up to 32mA, I didn’t believe that the Arduino outputs could actually sink that much current—20 mA is what the spec sheet allows. Checking with the BitScope, I see a 3840mV drop across the resistor, for 32mA. Note: I used pins D3 and D5 of the Arduino so that I could use pulse-width modulation (PWM) if I wanted to. (Schematic drawn with Digikey’s SchemeIt.)


Filed under: Circuits course, Data acquisition, freshman design seminar Tagged: Arduino, IR emitter, LED, phototransistor, pulse, pulse monitor, pulse oximeter

Optical pulse monitor with little electronics

In yesterday’s blog post, I talked mainly about what my son did with his time yesterday, to mention the small amount of debugging help I gave him.  Today I’ll post about what I did with most of my time yesterday.

This year, I am hoping to lead a 2-unit freshman design seminar for bioengineering students.  (Note: I did not say “teach” here, as I’m envisioning more of a mentoring role than a specific series of exercises.)  One thing I’m doing is trying to come up with design projects that freshmen with essentially no engineering skills can do as a team.  They may have to learn something new (I certainly hope they do!), but they should only spend a total of 60 hours on the course, including class time.  Since I want to spend some of class time on lab tours, lab safety, using the library resources, and how to work in a group effectively, there is not a lot of time left for the actual design and implementation.

One of the things I found very valuable in designing the Applied Circuits course was doing all the design labs myself, sometimes several times, in order to tweak the specs and anticipate where the students would have difficulty.  I expect to do some redesign of a couple of the circuits labs this year, but that course is scheduled for Spring (and finally got official approval this week), while the (not yet approved) freshman seminar is scheduled for Winter.  So I’m now experimenting with projects that I think may be suitable for the freshman design seminar.

These students cannot individually be expected to know anything useful, high school in California being what it is.  As a group, though, I think I can expect a fair amount of knowledge of biology, chemistry, and physics, with perhaps a sprinkling of math and computer programming.  I can’t expect any electronics knowledge, and we won’t have access to a machine shop—we may get permission for the students to use a laser cutter under supervision.  We can probably get some space in an electronics lab, but maybe not in a bio lab (the dean took away the department’s only teaching lab, with a “promise” to build a bigger one—but it is unlikely to be available for the freshmen by Winter quarter—I miss our first dean of engineering, as we seem to have had a series of incompetent deans since then).

So I’m looking for projects that can essentially be built at home with minimal tools and skills, but that are interesting enough to excite students to continue to higher levels in the program.  And I want them to be design projects, not kit-building or cookbook projects, with multiple possible solutions.

So far, there have been a couple of ideas suggested, both involving a small amount of electronics and some mechanical design:

  • An optical growth meter for continuously monitoring a liquid culture of bacteria or yeast. The electronics here is just a light source (LED or laser diode with current-limiting resistor), a phototransistor,  a current-to-voltage converter for the phototransistor (one resistor), and a data logger (like the Arduino Data Logger we use for the circuits course).  The hard part is coming up with a good way to get uniform sampling of the liquid culture while it is in an incubator on a shaker table.  There are lots of possible solutions: mounting stuff around flasks, immersed sensors, bending glass tubing so that the swirling culture is pumped through the tubes, external peristaltic pumps, … .  Design challenges include how the parts of the apparatus that touch the culture will be sterilized, how to keep things from falling apart when they are shaken for a couple of days, and so forth.   I’ve not even started trying to do a design here, though I probably should, as the mechanical design is almost all unfamiliar to me, and I’d be a good example of the cluelessness that the students would bring to the project.
  • An optical pulse sensor or pulse oximeter.  This is the project I decided to work on yesterday. The goal is to shine light through a finger and get a good pulse signal.  (I tried this last summer, making a very uncomfortable ear clip and doing a little testing before rejecting the project for the circuits course.)  If I can get good pulse signals from both red and IR light sources, I should be able to take differences or ratios and get an output proportional to blood oxygenation.

I decided yesterday to try to build a pulse monitor with almost no electronics.  In particular, I wanted to try building without an op amp or other amplifier, feeding the phototransistor signal directly into an Arduino analog in.  (I may switch to using the KL25Z for this project, as the higher resolution on the analog-to-digital converter means I could use smaller signals without amplification.)

A phototransistor is essentially a light-to-current converter.  The current through the phototransistor is essentially linear in the amount of light, over a pretty wide range. The Arduino analog inputs are voltage sensors, so we need to convert the current to a voltage.  The simplest way to do this is to put a series resistor to ground, and measure the voltage across the series resistor.  The voltage we see is then the current times the resistance.  Sizing the resistor is a design task—how big a current do we get with the intensity of light through the finger, and how much voltage do we need. The voltage needed can be estimated from the resolution of the analog-to-digital converter, but the amount of light is best measured empirically.

One problem that the pulse monitor faces is huge variations in ambient light.  Ideally the phototransistor gets light only from LED light shining through the finger, but that is a bit hard to set up while breadboarding.  Distinguishing the ambient light from the light through the finger can be difficult. Yesterday, I tried to reduce that problem by using “synchronous decoding”.  That is, I turned the LED on and off, and measured the difference between the phototransistor current with the LED on and with the LED off.  Using the Arduino to control the LED as well as to read the voltage is fairly easy—these are the sorts of tasks that are starter projects on the Arduino, so should be within the capabilities of the freshmen (with some learning on their part).

I also looked at the phototransistor output with my BitScope oscilloscope, so that I could see the waveform that the Arduino was sampling two points from.  Here is an example waveform:

The x-axis is 20ms/division, and the y-axis 1v/division, with the center line at 2v.
I put in a 50% duty cycle (20ms on, 20ms off).  The IR light is shining through my index finger.

For the above trace, I used a 680kΩ pulldown resistor to convert the current to voltage. I played a lot with different resistors yesterday, to get a feel for the tradeoffs.  Such a large resistor provides a large voltage swing for a small change in current, but the parasitic capacitance makes for rather slow RC charge/discharge curves.  Using larger resistors does not result in larger swings (unless the frequency of the input is reduced), because the RC time constant gets too large and the slowly changing signal does not have time to make a full swing.  I tried, as an experiment, adding a unity-gain buffer, so that the BitScope and Arduino inputs would not be loading the phototransistor.  This did not make much difference, so most of the parasitic capacitance is probably in the phototransistor itself.  One can get faster response for a fixed change in light only by decreasing the voltage swing, which would then require amplification to get a big enough signal to be read by the Arduino.  (It may be that the extra 6 bits of resolution on the KL25Z board would allow a resistor as low as 20kΩ and much faster response.)

Note that ambient light results in a DC shift of the waveform without a change in shape, until it gets bright enough that the current is more than 5v/680kΩ (about 7µA), at which point the signal gets clipped.  Unfortunately, ordinary room lighting is enough to saturate the sensor with this large a series resistor.  I was able to get fairly consistent readings by using the clothespin ear clip I made last summer, clamped open to make it the right size for my finger.  I did even better when I put the clip and my hand into a camera bag that kept out most of the ambient light.  Clearly, mechanical design for eliminating ambient light will be a big part of this design.

One might think that the 2v signal seen on the BitScope is easily big enough for pulse detection, but remember that this is not the signal we are interested in.  The peak-to-peak voltage is proportional to how transparent the finger is—we are interested in the variation of that amplitude with blood flow.  Here is an example plot of the sort of signal we are looking at:

(click to embiggen) The pulse here is quite visible, but is only about a 15–30 count change in the 300-count amplitude signal. Noise from discretization (and other sources) makes the signal hard to pick out auotmatically.  This signal was recorded with the Arduino data logger, but only after I had modified the data logger code to turn the IR emitter on and off and report differences in the readings rather than the readings themselves. Note the sharp downward transition (increased opacity due to more blood) at the beginning of each pulse.

To get a bigger, cleaner signal, I decided to do some very crude low-pass filtering on the Arduino. I used the simplest of infinite-impulse response (IIR) filters: . Because division is very slow on the Arduino, I limited myself to simple shifts for division: a= 1/2, 1/4, or 1/8. To avoid losing even more precision, I actually output then divided by 8 to get Y(t). I also used a 40msec sampling period, with the IR emitter on for 20ms, then off for 20msec (the waveform shown in the oscilloscope trace above).

(click to embiggen) With digital low-pass filtering, the pulse signal is much cleaner, but the sharp downward transition at the start of each pulse has been rounded off by the filter. This data was not captured with the Arduino Data Logger, but by cutting and pasting from the Arduino serial monitor, which involves simpler (hence more feasible for freshmen) programming of the Arduino.

I now have a very clean pulse signal, using just the Arduino, an IR emitter, a phototransistor, and two resistors. There is still a huge offset, as the signal is 200 counts out of 4600, and the offset fluctuates slowly.  To get a really good signal, I’d want to do a bandpass filter that passes 0.3Hz to 3Hz (20bpm–200bpm), but designing that digital filter would be beyond the scope of a freshman design seminar.  Even the simple IIR filter is pushing a bit here.

I’m not sure how to go from here to the pulse oximeter (using both an IR and a red LED) without fancy digital filtering.  Here is the circuit so far:

Although the 120Ω resistor allows up to 32mA, I didn’t believe that the Arduino outputs could actually sink that much current—20 mA is what the spec sheet allows. Checking with the BitScope, I see a 3840mV drop across the resistor, for 32mA. Note: I used pins D3 and D5 of the Arduino so that I could use pulse-width modulation (PWM) if I wanted to. (Schematic drawn with Digikey’s SchemeIt.)


Filed under: Circuits course, Data acquisition, freshman design seminar Tagged: Arduino, IR emitter, LED, phototransistor, pulse, pulse monitor, pulse oximeter

Freedom KL25Z board

On Tuesday, 2013 July 16, I ordered Freescale’s new $13 KL25Z development board from Digi-Key as part number FRDM-KL25Z-ND, they shipped it within an hour and a half, and I got it on Thursday, 2013 July 18 (shipping charges were $3.43, but I had ordered a few other parts at the same time).

My KL25z board with a USB cable plugged into the SDA USB port. This is the port used for downloading. The other USB port is used for serial communication, mouse or keyboard emulation, and so forth.

The board does not come with any headers, so I’ll need to buy a 2×6, 2 2×8, and a 2×10 female header to solder in.  These will add about $3.36  $5 to the cost of the board—more since I neglected to order them with the board and so will have to pay shipping again.   The headers would cost $2.05  $3.07 in hundreds, which would be the right quantity to buy if we used the boards in a lab course.  (I had the wrong female headers selected before—the 2×3, 2×4, and 2×5.) Adding headers would have added about $2.50 $4.50 to the cost of making the board (either adding a wave-soldering step or using more expensive surface-mount headers), so you can see why they left the connectors off in a $13 board.  Besides, for many applications, engineers would prefer to solder wires to the board, rather than using headers.

The lack of headers is a bit of a problem for using the boards as data acquisition devices in the Applied Circuits course, as the students have to be able to data acquisition in the first week, but we don’t get to soldering until later in the course.  I could make the KL25Z boards be an option, for those who already know how to solder.

The technique for downloading to the board is elegant: if you plug a USB cable into the SDA USB port on the board, the board looks like a flash drive to the computer.  Dragging a binary file to that drive causes the file to be loaded into the flash memory of the MKL25Z128VKL4 processor on the board.

Unfortunately, one thing that Freescale doesn’t tell you about the board is that they ship it with a bootloader  and flash-drive downloader written by PE Micro, and this firmware only works with Windows computers, not with Linux or Mac computers.  PE Micro claims that version 1.12 of their firmware fixes this terrible bug, but that doesn’t do much good if (like me) you are shipped a board with version 1.09 of the bootloader.

Luckily there is a workaround, explained by MBED: do a bootloader download of the MBED flash-drive downloader using a Windows machine.  Thereafter you can do the drag-and-drop downloads of programs as intended, even from Linux and Mac computers.  The explanation by MBED is not completely clear, as it implies that you only have to do this if the BOOTLOADER does not appear as a flash drive on the Mac, but it seems to be necessary even with the BOOTLOADER visible.  Presumably, loading v 1.12 of the PE micro software would work also, but I haven’t tested that (and any company that releases 11 non-functional versions before testing on a Mac or Linux machine has lost all my confidence in their software development ability). I took the board into work yesterday and downloaded the MBED firmware using a Windows machine, and I now have no problem dragging and dropping software using my Mac.

The MBED system is an interesting tool chain for developing C/C++ software for ARM processors.  They provide an open-source software development kit (SDK) and a free online compiler and integrated development environment,which includes an online compiler, a version control system, easy import of libraries and example code, and easy public release of code to other users of the system.

The reason for this online compilation is  probably that the ARM RVDS 4.1 compiler they use is terribly expensive—ARM does not give a price, but uses the “get a quote” system, which almost always means that prices are astronomical.  If you have to ask, you can’t afford it.  Freescale also sells a development system called CodeWarrior, at various prices, including a free version.  But the free versions, at least, are only available as exe files for Windows, and so useless to me.

It should be possible to use the gnu gcc compiler for the ARM processors, but I’ve not investigated how to set it up for the KL25Z board.  My son is looking into this, as he does not like online compilers and idiosyncratic IDEs, preferring to use familiar editors, command lines, and Makefiles for his compilation (as I do).  If he gets the gnu compiler set up for the board we can export any libraries and programs from the online compiler to our own machine and use the gcc compiler.  We can also compare code size between the ARM compiler and the gcc compiler.  I suspect that the ARM compiler, whose code generator can be tailored more closely to the ARM architecture, produces smaller, faster code, but that is unlikely to matter in any of our applications.

One problem I see with the free toolchains for the KL25Z board, compared to the Arduino IDE, is that there is no equivalent of the Arduino serial monitor, so debugging a downloaded program may be difficult.  If we don’t find a serial monitor already written, my son or I may write one in Python (based on the serial communication stuff he wrote for the Arduino Data Logger).  The MBED site provides easy sharing of ARM code, but nothing for sharing code that lives on the host at the other end of a USB cable.

The MBED SDK provides an Application Programming Interface (API) that is at about the same level of abstraction as the Arduino API, so programming should be fairly easy.  I plan to do a little playing with the board this weekend, just using the on-board peripherals (capacitive touch-sensor slider, 3-axis accelerometer, 3-color LED, and USB port).  I can’t really do any work this weekend anyway, because campus had a power failure Friday night and the file servers I use won’t be rebooted until Monday.

[Update: 2:30 pm: I’m not going to be doing any development work on the KL25Z this weekend after all.  I had the device mounted as BOOTLOADER (to check the version number of the bootloader for this post), and accidentally dragged a file to it, downloading a file that overwrote the MBED program and rendering the device once again invisible to Macs (except for the destroy-only BOOTLOADER).  I’m going to have to wait until Monday to go back to work and reload the MBED program using a Windows box.

Until Freescale starts shipping the boards with a properly functioning bootloader, I’m afraid I can’t really recommend the boards.  Having to find a Windows machine every time I make a careless error like this is just too big an irritant.]


Filed under: Circuits course, Data acquisition Tagged: Arduino, data logger, KL25Z

Freedom KL25Z board

On Tuesday, 2013 July 16, I ordered Freescale’s new $13 KL25Z development board from Digi-Key as part number FRDM-KL25Z-ND, they shipped it within an hour and a half, and I got it on Thursday, 2013 July 18 (shipping charges were $3.43, but I had ordered a few other parts at the same time).

My KL25z board with a USB cable plugged into the SDA USB port. This is the port used for downloading. The other USB port is used for serial communication, mouse or keyboard emulation, and so forth.

The board does not come with any headers, so I’ll need to buy a 2×6, 2 2×8, and a 2×10 female header to solder in.  These will add about $3.36  $5 to the cost of the board—more since I neglected to order them with the board and so will have to pay shipping again.   The headers would cost $2.05  $3.07 in hundreds, which would be the right quantity to buy if we used the boards in a lab course.  (I had the wrong female headers selected before—the 2×3, 2×4, and 2×5.) Adding headers would have added about $2.50 $4.50 to the cost of making the board (either adding a wave-soldering step or using more expensive surface-mount headers), so you can see why they left the connectors off in a $13 board.  Besides, for many applications, engineers would prefer to solder wires to the board, rather than using headers.

The lack of headers is a bit of a problem for using the boards as data acquisition devices in the Applied Circuits course, as the students have to be able to data acquisition in the first week, but we don’t get to soldering until later in the course.  I could make the KL25Z boards be an option, for those who already know how to solder.

The technique for downloading to the board is elegant: if you plug a USB cable into the SDA USB port on the board, the board looks like a flash drive to the computer.  Dragging a binary file to that drive causes the file to be loaded into the flash memory of the MKL25Z128VKL4 processor on the board.

Unfortunately, one thing that Freescale doesn’t tell you about the board is that they ship it with a bootloader  and flash-drive downloader written by PE Micro, and this firmware only works with Windows computers, not with Linux or Mac computers.  PE Micro claims that version 1.12 of their firmware fixes this terrible bug, but that doesn’t do much good if (like me) you are shipped a board with version 1.09 of the bootloader.

Luckily there is a workaround, explained by MBED: do a bootloader download of the MBED flash-drive downloader using a Windows machine.  Thereafter you can do the drag-and-drop downloads of programs as intended, even from Linux and Mac computers.  The explanation by MBED is not completely clear, as it implies that you only have to do this if the BOOTLOADER does not appear as a flash drive on the Mac, but it seems to be necessary even with the BOOTLOADER visible.  Presumably, loading v 1.12 of the PE micro software would work also, but I haven’t tested that (and any company that releases 11 non-functional versions before testing on a Mac or Linux machine has lost all my confidence in their software development ability). I took the board into work yesterday and downloaded the MBED firmware using a Windows machine, and I now have no problem dragging and dropping software using my Mac.

The MBED system is an interesting tool chain for developing C/C++ software for ARM processors.  They provide an open-source software development kit (SDK) and a free online compiler and integrated development environment,which includes an online compiler, a version control system, easy import of libraries and example code, and easy public release of code to other users of the system.

The reason for this online compilation is  probably that the ARM RVDS 4.1 compiler they use is terribly expensive—ARM does not give a price, but uses the “get a quote” system, which almost always means that prices are astronomical.  If you have to ask, you can’t afford it.  Freescale also sells a development system called CodeWarrior, at various prices, including a free version.  But the free versions, at least, are only available as exe files for Windows, and so useless to me.

It should be possible to use the gnu gcc compiler for the ARM processors, but I’ve not investigated how to set it up for the KL25Z board.  My son is looking into this, as he does not like online compilers and idiosyncratic IDEs, preferring to use familiar editors, command lines, and Makefiles for his compilation (as I do).  If he gets the gnu compiler set up for the board we can export any libraries and programs from the online compiler to our own machine and use the gcc compiler.  We can also compare code size between the ARM compiler and the gcc compiler.  I suspect that the ARM compiler, whose code generator can be tailored more closely to the ARM architecture, produces smaller, faster code, but that is unlikely to matter in any of our applications.

One problem I see with the free toolchains for the KL25Z board, compared to the Arduino IDE, is that there is no equivalent of the Arduino serial monitor, so debugging a downloaded program may be difficult.  If we don’t find a serial monitor already written, my son or I may write one in Python (based on the serial communication stuff he wrote for the Arduino Data Logger).  The MBED site provides easy sharing of ARM code, but nothing for sharing code that lives on the host at the other end of a USB cable.

The MBED SDK provides an Application Programming Interface (API) that is at about the same level of abstraction as the Arduino API, so programming should be fairly easy.  I plan to do a little playing with the board this weekend, just using the on-board peripherals (capacitive touch-sensor slider, 3-axis accelerometer, 3-color LED, and USB port).  I can’t really do any work this weekend anyway, because campus had a power failure Friday night and the file servers I use won’t be rebooted until Monday.

[Update: 2:30 pm: I'm not going to be doing any development work on the KL25Z this weekend after all.  I had the device mounted as BOOTLOADER (to check the version number of the bootloader for this post), and accidentally dragged a file to it, downloading a file that overwrote the MBED program and rendering the device once again invisible to Macs (except for the destroy-only BOOTLOADER).  I'm going to have to wait until Monday to go back to work and reload the MBED program using a Windows box.

Until Freescale starts shipping the boards with a properly functioning bootloader, I'm afraid I can't really recommend the boards.  Having to find a Windows machine every time I make a careless error like this is just too big an irritant.]


Filed under: Circuits course, Data acquisition Tagged: Arduino, data logger, KL25Z

Precision rectifier

The log amplifier that I’ve spent the last several days understanding (posts a, b, c) is not the only non-linear circuit needed for a loudness detector.  We also need to convert the audio input signal into a slowly changing amplitude signal that we can take the logarithm of.  As I discussed in the first post on log amps, I had rejected true-RMS converter chips as too expensive for the application (though the original application has gone away), and decided to try a rectifier and low-pass filter.

[Incidentally, my son is now looking at a different processor chip, the KL25 from Freescale (an ARM Cortex chip), which has a 16-bit ADC that is much faster than the ATMega’s, so the entire loudness-detector could be done in software, except for a one-op-amp preamplifier.  With a  16-bit ADC, we can get almost 90dB of range without needing a log amplifier. We’re planning to order a development board that is compatible with Arduino shields (but has lots more I/O pins available) and that has an accelerometer on the board.  Amazingly, the development board is only $13, about half the price of an Arduino.]

A single diode does not work for our rectifier needs in the loudness circuit, because diodes don’t turn on until the voltage across them is at least a “diode drop” (about 0.7v for silicon diodes and 0.45v for Schottky diodes).  However, the simple circuit for the log amplifier is also the circuit for a precision rectifier:

This circuit is both a log amplifier and a precision rectifier. If Vb is set to a constant voltage, then Vout1 varies as log(Vb–Va). Vout2 is max(Va,Vb). 
The diode can be connected in the opposite direction, to get Vout2=min(Va,Vb) and Vout1 varying with log(Va–Vb).

The log-amplifier circuit I used in the previous posts had a transistor in place of the diode, so that the crucial voltage that was being exponentiated was referenced to the bias voltage, rather than the input. We also needed a compensation capacitor in parallel with the transistor to prevent oscillation in the negative feedback loop.

For the precision rectifier, we swap the inputs, so that Va is the signal input and Vb is a constant threshold voltage for the rectifier. We do not need (or want) the compensation capacitor, as that would cause the circuit to act as a unity gain amplifier at high frequency, rather than as a rectifier.

Because I did not happen to have any diodes, but had plenty of transistors, I experimented with the rectifier circuit using diode-connected bipolar transistors (collector and base connected together). Because the output of the rectifier is not directly driven by an op amp, I used unity-gain buffers to isolate the test circuitry (Arduino analog inputs or BitScope oscilloscope) from the amplifier:

Test circuit for low-speed testing of precision rectifier circuit. Here the NPN transistor is used as a diode, in the opposite direction as in the first schematic, so Vout should be min(Vin, Vbias)

My initial test setup did not have the unity-gain buffer for Vin, but I found that one of my Arduino analog input pins was quite low impedance and was discharging my capacitor. Switching to a different pin helped, but I eventually decided to avoid that kluge and use a unity-gain buffer instead.

I tried several different values for R2. The most predictable effect of changing the value is a change in the range over which the rectifier operates. Clipping occurs because the output of the op amp is limited to be between the rails of the power supply. The feedback transistor is conducting when the rectifier is following the input, so the op amp output has to be substantially lower than Vout.  The function implemented is max(Vclip, min(Va,Vbias)).  Vclip should go down as R2 is increased (at about 60mV for each factor of 10 in resistance—the same shift as in the log amplifier).

Here are a couple of plots of Vout vs. Vin for the S9018 transistor:

(click to embiggen) With a 1kΩ resistor, the clipping voltage is fairly high, and we have a somewhat limited range for the rectifier.  The offset voltage for the rectifier between the output and the input is much less than the resolution of the Arduino ADC (about 5mV).

(click to embiggen) With a 10kΩ resistor, the clipping voltage is lower, giving us more range for the rectifier.

Using a PNP transistor instead of an NPN has the effect of reversing the diode and producing Vout=min(Vclip, max(Vin, Vbias)):

(click to embiggen) With an S9012 PNP transistor and a 1kΩ resistor, we get clipping at the high end.

(click to embiggen) With a 10kΩ resistor we get a larger range.

So why not go for a very large resistor and maximize the range? From a DC perspective this looks like a win (though it is hard to see in the limit how Vbias would affect the result if the resistance went to infinity).  Of course, the problem is with high-frequency response.  Consider the difference between the S9018 NPN transistor with a 1kΩ resistor and a 330kΩ resistor:

(click to embiggen) With an S9018 NPN transistor and a 330kΩ resistor at 1kHz. Note the overshoot when the rectifier shuts off.

(click to embiggen) Fairly clean signal with a S9018 NPN transistor and a 10kΩ resistor at 1kHz.

(click to embiggen) With a 1kΩ resistor, there is very little overshoot as the rectifier turns off, but we can see a bit of a problem when the rectifier turns back on.

There is a problem with the rectifier turning on slowly, because Vout has to move all the way from the top rail down to the bias voltage, and the op amp has a slew-rate limitation. This phenomenon can be seen more clearly at a higher frequency:

(click to embiggen) The S9018 NPN transistor with a 10kΩ resistor and a 15kHz input signal. The overshoot as the rectifier turns off is about 50mV, and the turn-on delay is about 8µsec. The turn-on delay does not vary much with the input resistance, unlike the turn-off overshoot.

I believe that the overshoot as the rectifier turns off is due to capacitance, as adding a small feedback capacitor in parallel with the diode increases the overshoot substantially:

(click to embiggen) With a 33pF capacitor in parallel with the diode (an S9018 NPN transistor), a 10kΩ resistor, and a 15kHz input, the overshoot goes up to about 290mV from 50mV without the capacitor. The turn-on delay is masked somewhat by the high-frequency feedback.

Note: the S9018 has the best high-frequency response (if you consider 15kHZ high frequency) of any of the transistors I looked, probably because it has the lowest capacitance. For example, with a 10kΩ resistor the S9013 NPN has 120mV of overshoot at 15kHz, instead of only 50mV, and the S9012 PNP has -180mV. With a 1kΩ resistor, I can’t measure the overshoot on any of these three transistors. So the limited range with a 1kΩ resistor is compensated for by the much cleaner turn-off behavior.  I should be able to get better range by using a fast-response Schottky diode instead of diode-connected transistor.

Of course, the turn-on behavior is a bigger problem and one that can’t be fixed by playing with the resistor or the diode, because the problem is with the large voltage swing needed from the op amp in order to turn on. There are standard solutions that limit the voltage swing, but I think I’ll leave that for a later blog post.


Filed under: Circuits course, Data acquisition Tagged: Arduino, BitScope, KL25Z, log amplifier, loudness, op amp, rectifier

Precision rectifier

The log amplifier that I’ve spent the last several days understanding (posts a, b, c) is not the only non-linear circuit needed for a loudness detector.  We also need to convert the audio input signal into a slowly changing amplitude signal that we can take the logarithm of.  As I discussed in the first post on log amps, I had rejected true-RMS converter chips as too expensive for the application (though the original application has gone away), and decided to try a rectifier and low-pass filter.

[Incidentally, my son is now looking at a different processor chip, the KL25 from Freescale (an ARM Cortex chip), which has a 16-bit ADC that is much faster than the ATMega's, so the entire loudness-detector could be done in software, except for a one-op-amp preamplifier.  With a  16-bit ADC, we can get almost 90dB of range without needing a log amplifier. We're planning to order a development board that is compatible with Arduino shields (but has lots more I/O pins available) and that has an accelerometer on the board.  Amazingly, the development board is only $13, about half the price of an Arduino.]

A single diode does not work for our rectifier needs in the loudness circuit, because diodes don’t turn on until the voltage across them is at least a “diode drop” (about 0.7v for silicon diodes and 0.45v for Schottky diodes).  However, the simple circuit for the log amplifier is also the circuit for a precision rectifier:

This circuit is both a log amplifier and a precision rectifier. If Vb is set to a constant voltage, then Vout1 varies as log(Vb–Va). Vout2 is max(Va,Vb). 
The diode can be connected in the opposite direction, to get Vout2=min(Va,Vb) and Vout1 varying with log(Va–Vb).

The log-amplifier circuit I used in the previous posts had a transistor in place of the diode, so that the crucial voltage that was being exponentiated was referenced to the bias voltage, rather than the input. We also needed a compensation capacitor in parallel with the transistor to prevent oscillation in the negative feedback loop.

For the precision rectifier, we swap the inputs, so that Va is the signal input and Vb is a constant threshold voltage for the rectifier. We do not need (or want) the compensation capacitor, as that would cause the circuit to act as a unity gain amplifier at high frequency, rather than as a rectifier.

Because I did not happen to have any diodes, but had plenty of transistors, I experimented with the rectifier circuit using diode-connected bipolar transistors (collector and base connected together). Because the output of the rectifier is not directly driven by an op amp, I used unity-gain buffers to isolate the test circuitry (Arduino analog inputs or BitScope oscilloscope) from the amplifier:

Test circuit for low-speed testing of precision rectifier circuit. Here the NPN transistor is used as a diode, in the opposite direction as in the first schematic, so Vout should be min(Vin, Vbias)

My initial test setup did not have the unity-gain buffer for Vin, but I found that one of my Arduino analog input pins was quite low impedance and was discharging my capacitor. Switching to a different pin helped, but I eventually decided to avoid that kluge and use a unity-gain buffer instead.

I tried several different values for R2. The most predictable effect of changing the value is a change in the range over which the rectifier operates. Clipping occurs because the output of the op amp is limited to be between the rails of the power supply. The feedback transistor is conducting when the rectifier is following the input, so the op amp output has to be substantially lower than Vout.  The function implemented is max(Vclip, min(Va,Vbias)).  Vclip should go down as R2 is increased (at about 60mV for each factor of 10 in resistance—the same shift as in the log amplifier).

Here are a couple of plots of Vout vs. Vin for the S9018 transistor:

(click to embiggen) With a 1kΩ resistor, the clipping voltage is fairly high, and we have a somewhat limited range for the rectifier.  The offset voltage for the rectifier between the output and the input is much less than the resolution of the Arduino ADC (about 5mV).

(click to embiggen) With a 10kΩ resistor, the clipping voltage is lower, giving us more range for the rectifier.

Using a PNP transistor instead of an NPN has the effect of reversing the diode and producing Vout=min(Vclip, max(Vin, Vbias)):

(click to embiggen) With an S9012 PNP transistor and a 1kΩ resistor, we get clipping at the high end.

(click to embiggen) With a 10kΩ resistor we get a larger range.

So why not go for a very large resistor and maximize the range? From a DC perspective this looks like a win (though it is hard to see in the limit how Vbias would affect the result if the resistance went to infinity).  Of course, the problem is with high-frequency response.  Consider the difference between the S9018 NPN transistor with a 1kΩ resistor and a 330kΩ resistor:

(click to embiggen) With an S9018 NPN transistor and a 330kΩ resistor at 1kHz. Note the overshoot when the rectifier shuts off.

(click to embiggen) Fairly clean signal with a S9018 NPN transistor and a 10kΩ resistor at 1kHz.

(click to embiggen) With a 1kΩ resistor, there is very little overshoot as the rectifier turns off, but we can see a bit of a problem when the rectifier turns back on.

There is a problem with the rectifier turning on slowly, because Vout has to move all the way from the top rail down to the bias voltage, and the op amp has a slew-rate limitation. This phenomenon can be seen more clearly at a higher frequency:

(click to embiggen) The S9018 NPN transistor with a 10kΩ resistor and a 15kHz input signal. The overshoot as the rectifier turns off is about 50mV, and the turn-on delay is about 8µsec. The turn-on delay does not vary much with the input resistance, unlike the turn-off overshoot.

I believe that the overshoot as the rectifier turns off is due to capacitance, as adding a small feedback capacitor in parallel with the diode increases the overshoot substantially:

(click to embiggen) With a 33pF capacitor in parallel with the diode (an S9018 NPN transistor), a 10kΩ resistor, and a 15kHz input, the overshoot goes up to about 290mV from 50mV without the capacitor. The turn-on delay is masked somewhat by the high-frequency feedback.

Note: the S9018 has the best high-frequency response (if you consider 15kHZ high frequency) of any of the transistors I looked, probably because it has the lowest capacitance. For example, with a 10kΩ resistor the S9013 NPN has 120mV of overshoot at 15kHz, instead of only 50mV, and the S9012 PNP has -180mV. With a 1kΩ resistor, I can’t measure the overshoot on any of these three transistors. So the limited range with a 1kΩ resistor is compensated for by the much cleaner turn-off behavior.  I should be able to get better range by using a fast-response Schottky diode instead of diode-connected transistor.

Of course, the turn-on behavior is a bigger problem and one that can’t be fixed by playing with the resistor or the diode, because the problem is with the large voltage swing needed from the op amp in order to turn on. There are standard solutions that limit the voltage swing, but I think I’ll leave that for a later blog post.


Filed under: Circuits course, Data acquisition Tagged: Arduino, BitScope, KL25Z, log amplifier, loudness, op amp, rectifier

Still more on log amplifiers

Yesterday, I spent the day testing different transistors in the log amplifier, to see whether it made much difference which transistor I used.  I wanted to test all 11 transistors in the iteadstudio assortment, the 5 PNP transistors and the 6 NPN transistors, to see if it made much difference and whether one of the transistors would give me a larger mV/dB scaling than the others.

Here is the test circuit I made (essentially the same as for the tests in Logarithmic amplifier again):

(click image to embiggen) The same circuit can be used for either NPN or PNP transistors. The only difference is whether the 470µF capacitor starts at 0v (for PNP) or 5v (for NPN) before decaying to Vbias through the 16kΩ resistor R5. For many of the tests the voltage-to-current resistor R2 was 1kΩ rather than 10kΩ.

The op amp at the top is a unity-gain buffer to provide a steady Vbias source that capable of providing some current (unlike the TL431ILP voltage reference shown here as an adjustable Zener diode). The nominal 2.5V reference was closer to 2.48V, which is within the ±2% spec for the part.

The unity-gain buffer on the left is to make the load on the RC circuit as high impedance as possible, so as not to disrupt the RC charging or discharging. The input current of the MCP6002 is typically ±1pA, which is far smaller than other sources of error (such as the parallel resistance of the capacitor itself). The parallel resistance of the capacitor makes the destination voltage of the RC circuit slightly lower than Vbias, which means that we will not get all the way to Vbias when testing PNP transistors, but will overshoot slightly when testing NPN transistors. (We should be able to reverse that by putting the capacitor between +5V and the switch, rather than ground and the switch.)

The right-hand op amp is just a non-inverting amplifier with 3× gain, to get better precision on measuring the output voltage with the Arduino ADC.  I made some measurements with Vout but most with the 3Vout signal. The scaling factor was 3.0 much more accurately than the repeatability of my fits to the data, so the gain in precision was not accompanied by a loss of accuracy. Ideally, I’d like to be able to use more than a 3× gain in the final stage, to get better precision, but I’m limited by the offset voltage of the log amplifier, which is determined by the transistor and by the voltage-to-current converter R2.

The middle op amp is the log amplifier itself, which relies on the exponential relationship of the emitter current to the voltage across the base-emitter junction. More precisely, we can use the Ebers-Moll model of a bipolar transistor to get

The collector is held at Vbias by the feedback loop of the op amp, so VBC is zero, simplifying the equation to

Assuming that βR and IS are constant, and that VBE is very large compared to VT (about 23 times bigger in my measurements), we have the desired exponential relationship: for some constant x.  Note that if we leave the –1 in, then we’ll have a small DC offset to iC.

(I believe that all this analysis is correct for NPN transistors, where VBE is positive, but that some signs may need to be negated for PNP transistors.)

The log-amplifier output is temperature-dependent, since , which is about 26mV at 300˚K. The scale factor can be multiplied by ln(10) to get 60mV/decade or 3mv/dB.

Note that this scaling is not affected by the current gain β of the transistor—that only affects the offset of the output voltage. This analysis agrees with the statement in Wikipedia, “At room temperature, an increase in VBE by approximately 60 mV increases the emitter current by a factor of 10.” There is probably an even bigger temperature dependence for the offset of the output than for the scaling, because of changes in βR and IS, but that is not included in the Ebers-Moll model.

The theoretical result indicates that I should get about 3mV/dB for any transistor, but that the offset voltage will vary depending on the characteristics of the transistor.  I might also run into effects not included in the Ebers-Moll model, especially at very large or very small collector currents.

Adjusting R2 to change the current can move the output offset around. If I make R2 large and the current small, then VBE will be small, and the approximation will be poor. If I make R2 small, the current may exceed what the transistor is designed for and there may be saturation effects.

Looking at typical collector currents on the I-vs-V plots for the transistors, I’m seeing values like 8–20mA at the high end, so with a 2.5V drop across R2, I want R2 to be at least 300Ω. Initially, I picked 1kΩ, as providing a large current at 2.5V, hoping that this would give a large dynamic range.  Here are a couple of good examples of measurements using the 3× output and R2=1kΩ.

(click image to embiggen) Typical 3× Vout vs Vin curve for a PNP transistor, here the A1015, using R2=1kΩ. This uses Arduino-measured Vin, Vout, and Vbias, so is limited to about 2 decades (40dB).

(click image to embiggen) Typical 3× Vout vs. Vin curve for an NPN transistor, using R2=1kΩ. Again we’re limited by the Arduino ADC to about 40dB of dynamic range.

The exponential decay of the capacitor to about Vbias allows us to extend the range of the fit well past the resolution of the ADC to measure Vin or Vbias. Combining the formulas for the log amplifier and the RC discharge gives us the general formula
.
Fitting the constants for this turns out to be difficult, because v3 is close to zero. If it were exactly zero, the formula would be , and we could make arbitrary tradeoffs between v2 and RC.

We can get a nice plot of Vout vs. time as the capacitor decays toward Vbias, particularly for the NPN transistor S9013:

(click picture to embiggen) The S9013 log amplifier shows what looks like a good fit over about 80dB, when using R2=1kΩ, and good linearity for about 65dB. The upward tail shows where the collector-base junction begins to be forward biased, and the current is no longer controlled by the base-emitter voltage.

Note that this curve shows that the problems we had with direct measurement of RC discharge curves in the physics lab was due to limitations of the Arduino ADC, not to the underlying RC circuit. The tails of the discharge continue to follow the exponential well beyond the resolution of the 10-bit ADC in the Arduino.

Of course, I picked out the S9013 plot to show, because it was the nicest one. Some of the others were weird. For example, consider the 2N5551:

(click image to embiggen) Not that the 2N5551 Vout vs Vin curve is not a simple logarithm—there is some sort of saturation happening for large input voltages, so the straight-line fit is awful.

The same 2N5551 transistor with R2 at 10kΩ behaves much better:

(click image to embiggen) With a 10kΩ resistor, the currents through the 2N5551 transistor are smaller, and no clipping occurs.

Even weirder was the behavior of the S9018 NPN transistor (the only transistor in the set not matched by a corresponding PNP transistor):

(click image to embiggen) With R2=1kΩ, the S9018 transistor shows a flat spot in the response for  277mV < Vin-Vbias < 330mV (that is, 277uA < IC < 330uA) . I have no idea what causes this flat spot.

I’m still mystified by the flat spot in the S9018 response—anyone have any ideas??

By changing R2 to 10kΩ, we can push the flat spot to 10× higher voltage, just outside the input range that the log amplifier uses:

(click image to embiggen) With R2=10kΩ, the response of the amplifier with a S9018 transistor is nicely logarithmic.

The time response to the RC discharge also looks good:

(click to embiggen) With R2=10kΩ, we get a good fit for about 76dB on the S9018 transistor. The larger resistor gives a somewhat softer turn on for the transistor if we go past Vbias.

All of my transistors gave mV/dB scaling that was about the same (just under 9 mV/dB after the 3× gain, so just under 3mV/dB for the unamplified output), as is predicted by the Ebers-Moll model.  I got better dynamic ranges for the NPN transistors than for the PNP transistors, but this may have been due to artifacts of the test setup.  In any case, it looks like 60–70dB ranges are fairly easily achieved.

[UPDATE 2013 July 15: I know I said I was done with the log amplifiers, but I had to do just one more test.  In addition to the 16kΩ resistor to Vbias, I added a 5.7MΩ resistor to +5V, giving me a slightly higher target voltage so that I had some overshoot when testing PNP transistors.  With this setup I could test the S9012 PNP transistor for 82dB with R2=1kΩ, and 63dB with R2=10kΩ.  So the better dynamic range of the NPN was just an artifact of my test setup, as I thought.]

If one were to try to make a measuring instrument with a log amplifier, there would have to be some temperature compensation as the log-amplifier offset and scaling are both temperature sensitive.  Having a temperature-independent voltage source for calibration would be a good idea.

I think I’m about burned out on log amplifiers now.  Perhaps later his week I’ll try doing some precision rectifier circuits.


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