Table of Contents
- Introduction
- High-Level View
- Principle of Operation
- Circuit Diagram
- DSP Block Diagram
- Building It
- Programming It
- Using It
- Calculations
- Results
- Result A – 10 ohm DUT
- Result B: 10 ohm and 220nF in Parallel
- Result C: 10 ohm and 100nF in Parallel
- Result D: 5 ohm Resistor
- Result E: 10 ohm and 2.2uF electrolytic in Parallel
- Result F: 10 ohm and 10uF electrolytic in Parallel
- Result G: 10 ohm and 10nF in parallel
- Result H: 33uH Inductor
- Result I: 100 uH Inductor
- Result J: 470uF capacitor
- Summary
Introduction
I had some time to experiment with the Wave Miner and decided to see if impedance and LCR measurement was possible, with a plug-on board. Most of the heavy lifting is done by the DSP board and the Pi, which between themselves perform the number-crunching. The analog side really is just a few low-cost op amps currently on a perf board, plugged on top of the Wave Miner. The green socket in the middle is where you plug the components to measure.
The information on how to build the Wave Miner is in a blog post, and there is a set of experiments for it.
To be clear, this isn't a blog about a complete ready-to-use LCR meter project, and there could be errors in it. It's just a proof-of-concept at best. A full LCR meter from this project might be feasible with more work, or alternatively, there are use-cases for impedance measurement over a certain range, in which case perhaps this project could help.
There are plenty of other LCR meter projects which could immediately be more capable, and it would be cheaper to just buy a meter than spend months fully developing one. This project is really just an experiment for a bit of a hardware/software/maths challenge.
The results from this project might not be correct or accurate or might fail over certain ranges. Any comments or even collaboration would be appreciated to improve the results because it will take more than me to figure out how to get the project into a more usable state for a variety of impedance or LCR measurement use-cases. There are a lot of refinements to be made, perhaps completely revamp the circuit too.
High-Level View
The project consists of a device-under-test (DUT), placed in a potential divider, and the source and potential divider output are connected to the Wave Miner board. The green socket in the center of the board is where the DUT can be inserted. Multiple components could be inserted, since the contacts in each row are wired together.
The Wave Miner consists of a ready-made DSP module from Aliexpress (it is the green module in the photo below), with the pins brought out to analog in/out connectors and an I2C connection to the Pi via a ribbon cable.
The potential divider top part comprises of a known resistance, Rtop, which for now is a 1 kohm resistor. The device-under-test (DUT) would be the unknown component for which the result is required. During a calibration phase, the DUT could be replaced with (say) a known resistance, or a short or open.
The actual project includes buffers and amplification between the potential divider and the Wave Miner; I'm using a couple of op-amps for that currently.
The initial implementation will use a Pi, since it's needed to program the Wave Miner, and it will allow easier development of the software. For a real use-case the Pi could be replaced with (say) a Pi Pico, and an LCD screen. The Wave Miner hardware is cheap enough that standalone hardware could be made with a microcontroller.
Principle of Operation
The diagram below shows the general idea. The orange parts are implemented inside the DSP.
The potential divider source (stimulus) would be an oscillator inside the DSP, set to a frequency like 1 kHz. The phase shift and amplitude would be measured using two multipliers within the DSP. The effect of the multipliers would be to provide an output that increases if the signal is in-phase with the original source oscillator or the 90-degree shifted version of it. The benefit of using the multiplier is that it becomes easy to reduce the effects of noise, because all other frequency components will be at a non-DC level (i.e. easily filtered), compared to the components at 1 kHz, which the multiplier will convert to a constant DC level.
The Raspberry Pi will be responsible for taking the two measurements, called real and imaginary, since the 90-degree measurement represents reactance which is traditionally on the y-axis or imaginary axis, on a phase plot that uses complex numbers. The Pi will be responsible for converting the values into resistance and capacitance and inductance, and other quantities derivable from the measurements.
Refer to the following blog post for an explanation of reactance: Measuring Capacitor Characteristics
There is an LTSpice file that can be used to simulate a potential divider. To use the file, install LTSpice, open the file with it, and then click the 'Running Man' icon. A table of values will be generated, and the magnitude and phase of the potential divider will be displayed. To modify the value of any component, or to modify the source stimulus voltage, right-click on the respective component.
Circuit Diagram
The current circuit diagram is shown below. The DUT connection is shown at the center, and the connections to the Wave Miner are on the left and right sides. In future, the value of Rtop could perhaps be switched, and the X100 amplifier could be switched off too (or value changed).
The left side source buffer is non-ideal since it cannot drive capacitive loads well, but it was low-cost to experiment with for now.
There is no PCB layout for this project, it is too soon for that since the hardware could change depending on feedback/suggestions.
DSP Block Diagram
The diagram here shows the current DSP configuration. On the left side are the oscillators in pairs, preset to values such as 100 Hz, 120 Hz, 1000 Hz, and so on. There are two oscillators per frequency, with one set to 90-degree phase shift to the other, in each pair.
The Pi is able to select via I2C which oscillator is used, using a 'source select' switch shown just to the right of the oscillators.
The selected oscillator goes to the source output, as well as to a multiplier. The 90-degree phase-shifted version of the oscillator goes to a second multiplier. Both multipliers receive the input from the Wave Miner too (same input for both multipliers). Next, there is some low-pass filtering and envelope measurement, to try to determine the DC level. The cluster of blocks on the right side of the diagram are a set of registers that the Pi can read, to access these measurements. That part looks weird, it is an attempt to try to get more resolution out of the DSP, but I'm not sure I'm successful there. In any case, that's a more minor thing, since the DSP could always be replaced with one that provides higher-resolution data register outputs.
The second input to the Wave Miner is simply measured and the value is offered to the Pi. This allows the Pi to know the amplitude of the source stimulus.
Building It
The circuit was constructed to plug on top of the Wave Miner. The inputs and outputs on the Wave Miner board are available on pin headers, so it is straightforward to plug perf board on top. It's a bit fiddly because the perf board is upside-down since the pin header sockets are needed on the reverse side of the board.
Programming It
The code for this project is part of the source code that is already installed as part of the Wave Miner project. If you just want to look at it, then the specific file is called imp.cpp.
To install the code, follow the information on the Wave Miner blog or the associated waveminer GitHub page. In brief, the following commands will need to be typed on the Pi to install the code:
mkdir -p ~/development
cd ~/development
git clone https://github.com/shabaz123/waveminer.git
cd waveminer
make
Upload the firmware to the Wave Miner by typing:
./eeload -p imp.bin
Next, power-cycle the Wave Miner board for the firmware to take effect.
Using It
Plug a component (or more) into the DUT socket.
The code can now be run by typing:
./imp -i 3 -d 1
Where -i 3 selects oscillator # 3 (1 kHz) and -d 1 selects display mode # 1 (no other mode is implemented currently).
The measured results will be displayed on the screen along with a load of interim calculations. The final values displayed could be (say) the following for a 10 ohm resistor in parallel with a 100 nF capacitor:
DUT impedance (Z) at 1000.000000 Hz: 9.941812 ohm
DUT reactance: -1742.682453 ohm
DUT resistance: 9.941974 ohm
DUT capacitance: 91.327564 nF
When you first run it, the values will likely be wrong, since there are a couple of hard-coded values that would ordinarily come from calibration procedures, but the code has no calibration method menu today; the code is too raw for that yet. The hard-coded values are described in the calculations section below, so that you can manually edit the code to put the calibration values from the first run.
Calculations
The Pi is used to perform the final calculations. The procedure is described below in a series of steps, and it is possible to see in the source code all of these steps being performed. Please note I may be making wrong assumptions, and the method may have significant errors. Here is what the Pi does currently:
(1) Switches on the DSP tone to 1 kHz, and waits a second or so for things to settle.
(2) Reads the measurements (right side of the DSP block diagram), and applies a constant factor to the two measured values, which are named vreal and vimag. The constant is hard-coded and can be changed. Eventually, calibration procedures will determine the factor.
(3) Converts the two measured values into magnitude and phase using trigonometry
(4) Subtracts a hard-coded value from the phase, which effectively makes the phase zero for the 10 ohm resistor scenario. This is because I'm assuming the resistor to be pure resistance for now. The point of this step is to remove phase shift error from the system that isn't part of the DUT.
(5) Convert the magnitude and the modified phase value from step (4), into cartesian real, imag form again.
(6) Determine Vtop, which is the voltage across the top resistor, by phasor subtracting the result from step (5), from vstimpeak (vstimpeak is measured by the DSP and sent to the Pi).
(7) Determine the current through the top resistor, Itop = Vtop / Rtop where Rtop is a known 1000 ohm value.
(8) Determine the impedance Z to be Z = magnitude / Itop
(9) Since we know Itop passes through the DUT, then the code determines the current through the resistance and reactance, using sine and cosine, i.e. resistance current is itop * cos(phase) and reactance current is itop * sin(phase). From that, the code determines the resistance and reactance, to be the magnitude divided by those values.
(10) Now the formula X=1/(2*PI*f*C) or X=2*PI*f*L is applied to convert reactance (X) to capacitance or inductance, depending on if the sign is negative or positive.
There is an Excel spreadsheet that can be used to try out the calculations. The screenshot below shows the calculations for Result C: 10 ohm and 100nF in Parallel shown further below.
Results
Result A – 10 ohm DUT
Here is an execution result when the DUT is a 10 ohm resistor. The first dozen or so lines contain I2C transaction information that is used to select the oscillator, and to read the measurements as raw bytes.
pi@raspberrypi:~/development/waveminer$ ./imp -i 3 -d 1
Selecting frequency 1000.000000 Hz
Selecting display format #1
writing to address 0x0028
writing to address 0x081a node 0x0626
read 3 bytes: 0x00, 20, 49
writing to address 0x081a node 0x061a
read 3 bytes: 0x00, 05, 7f
using zoom
writing to address 0x081a node 0x0662
read 3 bytes: 0x0c, 9d, 0d
writing to address 0x081a node 0x066e
read 3 bytes: 0x02, 26, 00
writing to address 0x081a node 0x03d2
read 3 bytes: 0x01, e7, 13
Next, the source voltage is displayed, which is vstimpeak from the DSP block diagram, which is measured from the WM_IN2 connector on the circuit diagram shown earlier. The DSP reports it to be 1.379 V peak, which is not precisely what a multimeter would measure it to be (I measure 1.205V peak with my multimeter), but the DSP uses it's own algorithm, and I don't think it matters (I could be wrong) provided all measurements are made in the same way.
Source voltage: 1.379360 V peak
The vreal and vimag values are next shown, as measured by the DSP (raw values), and then scaled by a hard-coded factor:
Raw vreal, vimag values (Vpp): [0.355155, 0.146575]
Scaled vreal, vimag values: [0.012550, 0.005179]
The magnitude and phase are next derived, and then a hard-coded correction is applied to the phase, to get it to be close to zero:
mag, phase (rad) is [0.013576, 0.391414]
Corrected mag, phase (rad) is [0.013576, 0.000006]
Corrected real, imag is [0.013576, 0.000000]
The voltage named Vtop, across the source resistor Rtop, is displayed next. It is calculated by using phasor subtraction, by subtracting the real and imaginary values from the source stimulus vstimpeak value, where the vstimpeak value is considered to be the reference zero degree phase shift, regardless of whatever the actual phase shift is compared to the oscillators inside the DSP. I may have made an incorrect assumption here!
voltage across source resistor is 0.965755 Vrms
Next, the current through the circuit is calculated, to be Vtop divided by Rtop:
current through circuit is 1.365783 mA peak (0.965755 mA RMS)
The DUT impedance is calculated to be the magnitude of the real/imaginary measurements, divided by the current calculated above:
DUT impedance (Z) at 1000.000000 Hz: 9.940382 ohm
Now since we have the impedance, and we know the phase, the resistance and reactance are calculated; the resistance is as expected, close to 10 ohm, since the hard-coded values were tweaked until the resistance was this value:
DUT reactance: 1698706.762660 ohm
DUT resistance: 9.940382 ohm
And since we know the reactance and the frequency, the capacitance or inductance is displayed, depending on the sign of the reactance. The result is huge, and it's because the reactance value has significant error at these phase angles. I think the algorithm could be modified to just not display it, if it is clearly wrong.
DUT inductance: 270357578.141012 uH
Result B: 10 ohm and 220nF in Parallel
The result looks pretty reasonable:
Source voltage: 1.379327 V peak
Raw vreal, vimag values (Vpp): [0.353147, 0.151052]
Scaled vreal, vimag values: [0.012479, 0.005338]
mag, phase (rad) is [0.013572, 0.404181]
Corrected mag, phase (rad) is [0.013572, -0.012761]
Corrected real, imag is [0.013571, -0.000173]
voltage across source resistor is 0.965735 Vrms
current through circuit is 1.365756 mA peak (0.965735 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 9.937565 ohm
DUT reactance: -778.764870 ohm
DUT resistance: 9.938374 ohm
DUT capacitance: 204.368416 nF
Result C: 10 ohm and 100nF in Parallel
The result looks good here too:
Source voltage: 1.379011 V peak
Raw vreal, vimag values (Vpp): [0.354273, 0.148585]
Scaled vreal, vimag values: [0.012518, 0.005250]
mag, phase (rad) is [0.013575, 0.397125]
Corrected mag, phase (rad) is [0.013575, -0.005705]
Corrected real, imag is [0.013575, -0.000077]
voltage across source resistor is 0.965510 Vrms
current through circuit is 1.365437 mA peak (0.965510 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 9.941812 ohm
DUT reactance: -1742.682453 ohm
DUT resistance: 9.941974 ohm
DUT capacitance: 91.327564 nF
Result D: 5 ohm Resistor
I used two 10 ohm resistors in parallel for this test. The result looks fine here too.
Source voltage: 1.379183 V peak
Raw vreal, vimag values (Vpp): [0.178972, 0.073838]
Scaled vreal, vimag values: [0.006324, 0.002609]
mag, phase (rad) is [0.006841, 0.391295]
Corrected mag, phase (rad) is [0.006841, 0.000125]
Corrected real, imag is [0.006841, 0.000001]
voltage across source resistor is 0.970392 Vrms
current through circuit is 1.372342 mA peak (0.970392 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 4.985042 ohm
DUT reactance: 40034.139893 ohm
DUT resistance: 4.985042 ohm
DUT inductance: 6371631.256457 uH
Result E: 10 ohm and 2.2uF electrolytic in Parallel
Perhaps this result is ok, not sure!
Source voltage: 1.379249 V peak
Raw vreal, vimag values (Vpp): [0.327524, 0.186825]
Scaled vreal, vimag values: [0.011573, 0.006602]
mag, phase (rad) is [0.013324, 0.518381]
Corrected mag, phase (rad) is [0.013324, -0.126961]
Corrected real, imag is [0.013216, -0.001687]
voltage across source resistor is 0.965932 Vrms
current through circuit is 1.366034 mA peak (0.965932 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 9.753588 ohm
DUT reactance: -77.030002 ohm
DUT resistance: 9.832730 ohm
DUT capacitance: 2066.142260 nF
Result F: 10 ohm and 10uF electrolytic in Parallel
Again, perhaps this result is fine, but it's hard to know. Electrolytics don't have a well-defined spec so there could be errors.
Source voltage: 1.379354 V peak
Raw vreal, vimag values (Vpp): [0.200730, 0.248395]
Scaled vreal, vimag values: [0.007093, 0.008777]
mag, phase (rad) is [0.011285, 0.891129]
Corrected mag, phase (rad) is [0.011285, -0.499709]
Corrected real, imag is [0.009905, -0.005407]
voltage across source resistor is 0.968354 Vrms
current through circuit is 1.369460 mA peak (0.968354 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 8.240400 ohm
DUT reactance: -17.197226 ohm
DUT resistance: 9.388395 ohm
DUT capacitance: 9254.686780 nF
Result G: 10 ohm and 10nF in parallel
This result looks reasonable:
Source voltage: 1.379133 V peak
Raw vreal, vimag values (Vpp): [0.355108, 0.146771]
Scaled vreal, vimag values: [0.012548, 0.005186]
mag, phase (rad) is [0.013578, 0.391930]
Corrected mag, phase (rad) is [0.013578, -0.000510]
Corrected real, imag is [0.013578, -0.000007]
voltage across source resistor is 0.965594 Vrms
current through circuit is 1.365556 mA peak (0.965594 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 9.942843 ohm
DUT reactance: -19491.285389 ohm
DUT resistance: 9.942844 ohm
DUT capacitance: 8.165441 nF
Result H: 33uH Inductor
This was very wrong; I'm not expecting an accurate result however since the measured values are small with the current implementation, and this increases errors:
Source voltage: 1.379150 V peak
Raw vreal, vimag values (Vpp): [0.033743, 0.004972]
Scaled vreal, vimag values: [0.001192, 0.000176]
mag, phase (rad) is [0.001205, 0.146291]
Corrected mag, phase (rad) is [0.001205, 0.245129]
Corrected real, imag is [0.001169, 0.000292]
voltage across source resistor is 0.974379 Vrms
current through circuit is 1.377981 mA peak (0.974379 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 0.874625 ohm
DUT reactance: 3.603998 ohm
DUT resistance: 0.901577 ohm
DUT inductance: 573.594088 uH
Result I: 100 uH Inductor
Again this is very wrong; I need to get some larger value inductors. If the algorithm works with capacitance, then there really shouldn't be an issue with inductance, since only the last formula changes.
Source voltage: 1.379028 V peak
Raw vreal, vimag values (Vpp): [0.045144, 0.006395]
Scaled vreal, vimag values: [0.001595, 0.000226]
mag, phase (rad) is [0.001611, 0.140718]
Corrected mag, phase (rad) is [0.001611, 0.250702]
Corrected real, imag is [0.001561, 0.000400]
voltage across source resistor is 0.974016 Vrms
current through circuit is 1.377467 mA peak (0.974016 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 1.169620 ohm
DUT reactance: 4.714608 ohm
DUT resistance: 1.207364 ohm
DUT inductance: 750.353099 uH
Result J: 470uF capacitor
This looks lower than expected. It could be that the algorithm is working, but that the Rtop value is no longer suitable:
Source voltage: 1.379111 V peak
Raw vreal, vimag values (Vpp): [0.003906, 0.014352]
Scaled vreal, vimag values: [0.000138, 0.000507]
mag, phase (rad) is [0.000526, 1.305067]
Corrected mag, phase (rad) is [0.000526, -0.913647]
Corrected real, imag is [0.000321, -0.000416]
voltage across source resistor is 0.974952 Vrms
current through circuit is 1.378790 mA peak (0.974952 mA RMS)
DUT impedance (Z) at 1000.000000 Hz: 0.381206 ohm
DUT reactance: -0.481480 ohm
DUT resistance: 0.624045 ohm
DUT capacitance: 330553.338966 nF
Summary
A really experimental proof-of-concept was created to measure impedance and LCR parameters for unknown devices. This project needs a lot of work, and it could be a long time before something usable is developed. Working through this project can provide an appreciation of the type of computations that a real LCR meter needs to perform and the accuracy of measurements that it can operate at.
Any feedback is welcome.
Many thanks!