Author Topic: Homebrew Lock-In Amplifier  (Read 9755 times)

0 Members and 1 Guest are viewing this topic.

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #50 on: May 04, 2024, 09:59:13 pm »
« Last Edit: May 04, 2024, 10:07:21 pm by Picuino »
 

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6332
  • Country: ro
Re: Homebrew Lock-In Amplifier
« Reply #51 on: May 05, 2024, 08:02:34 am »
What I would try:
- split the 4k7 in two of 2k2 or so, one to the +5V, and one to the GND, with the DUT in the middle
- remove the transistor, and use 4 MCU pins in parallel instead of GND, and another 4 parallel pins as V+ for the 2k2+DUT+2k2 divider (the average Rds ON is about 60ohms for 1 DO, and the mismatch between the upper and the lower side is about 10% or less)
- once the divider connected between the MCU pins, alternate the polarity (output 11110000 then 00001111 repeatedly), and measure the peak to peak amplitude, such that the thermocouple voltages and opamp offset are eliminated
- trigger the ADC always at the same distance from the polarity flipping edge, somewhere at the middle
- use the low-noise mode conversion if possible (don't recall the exact conditions), the one in which other unnecessary blocks of the microcontroller are powered down, so to make less noise during a conversion
- keep all arithmetic with integers (maybe keep a running average on the MCU, not sure), and only use floating point to scale the received data on the PC
- use the highest possible clock for the CPU, and use a constant time loop (an infinite loop that always execute in the same amount of machine cycles rather than interrupts, if possible), to minimize the jitter
« Last Edit: May 05, 2024, 08:09:49 am by RoGeorge »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #52 on: May 05, 2024, 10:06:57 am »
Thank you very much RoGeorge.

I have an ADC AD7691: https://www.analog.com/media/en/technical-documentation/data-sheets/ad7691.pdf
My next step will be to sample with this more accurate adc that can separate the analog from the digital part.
With that I hope to get some more accuracy, but I'm afraid I'm not going to get much error reduction due to the noise. Anyway I will try to check the results.

I will take into account the advice to try to reduce jitter.

As for the signal directly from the digital outputs, it doesn't seem to be a good idea, because when I remove the transistor and feed the resistor under test directly from the digital output of the microcontroller, the noise increases.
I will try to separate as much as possible the analog part from the digital part.
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 14319
  • Country: de
Re: Homebrew Lock-In Amplifier
« Reply #53 on: May 05, 2024, 01:09:49 pm »
Using the µC to directly drive the DUT is a 2 sided thing: if eliminates the transistor and gets maybe closer to a ratiometric measurement. However it also add the somewhat temperature dependent output resistance. So the gain drift and fluctuations from that side can get worse.
With the current test current of some 1 mA  0,1 mOhm is  100 nV at the DUT. So the observed noise is not that bad.
It depends on the avearging time if the observed noise is about what one can expect from the AD8220. With a lock-in amplifier one can trade speed for noise. With relatively short integration one may still get some effect of mains hum.
 

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6332
  • Country: ro
Re: Homebrew Lock-In Amplifier
« Reply #54 on: May 05, 2024, 01:28:58 pm »
What are the aimed specs?  What range of resistance, with what accuracy, and at what resolution should the instrument measure?

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #55 on: May 05, 2024, 07:20:04 pm »
With the current test current of some 1 mA  0,1 mOhm is  100 nV at the DUT. So the observed noise is not that bad.
It depends on the avearging time if the observed noise is about what one can expect from the AD8220. With a lock-in amplifier one can trade speed for noise. With relatively short integration one may still get some effect of mains hum.
At this time the measurement time is approximately 1 second in which more than 9200 samples are taken.
100nV of noise is OK, but that is the residual noise after averaging all samples. In the individual samples the Vout signal coming out of the amplifier AD8220 has a noise greater than 20mV and I think it is very high. Anyway for a prototype board mounted circuit the final noise it is not too bad.

What are the aimed specs?  What range of resistance, with what accuracy, and at what resolution should the instrument measure?
For now I am just experimenting to see what can be achieved with this type of circuitry.
With what I have already achieved, the circuit would have a resolution of 0.1 milliohm and a range of about 4 ohms (this would require coupling the input of the instrumentation amplifier capacitively).
Good range and resolution for measure ESR of capacitors or measure the resistance of copper traces of a PCB.
« Last Edit: May 05, 2024, 07:23:09 pm by Picuino »
 

Offline jbb

  • Super Contributor
  • ***
  • Posts: 1155
  • Country: nz
Re: Homebrew Lock-In Amplifier
« Reply #56 on: May 05, 2024, 08:02:02 pm »
At risk of totally derailing the proceedings, I have an alternative suggestion: the Texas Instruments ADS1235. It has a 24 bit ADC, Programmable Gain Amplifier (PGA) and control logic for AC excitation. Seems like it can apply positive excitation, sample the ADC, apply negative excitation and sample the ADC again for you.

https://www.ti.com/product/ADS1235
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 14319
  • Country: de
Re: Homebrew Lock-In Amplifier
« Reply #57 on: May 05, 2024, 08:03:50 pm »
It is normal for the individual readings from the ADC to have quite some noise. Some noise is actually good, so that oversampling works as intended and is not stuck a certain ADC levels.

For low noise one may acutually want some anti aliasing filter (e.g. for some 5 -10 kHz). Other wise the µC internal ADC could pic up noise up to some 50 kHz (or 15 kHz from the amplifier). The limited BW of the AD8220 here also does some filtering, but more could improve things a little more.
The settling time of the fitler would likely want skipping some samples to reduce the effect of the settling speed.

20 mV at the output of an amplifier with a gain of 1000 is not that bad.
 

Offline trobbins

  • Frequent Contributor
  • **
  • Posts: 769
  • Country: au
Re: Homebrew Lock-In Amplifier
« Reply #58 on: May 06, 2024, 01:19:20 am »
Back in the 90's I used an AD630 to extract a battery impedance measurement from industrial noisy live battery systems using a portable tool that applied about 0.1Arms sinewave into a battery (cell or string) and kelvin probe extracted a signal that was amplified and presented to the AD630.   An ICL8038 sine generator provided reasonable amplitude stability, muting, and a sync signal for the AD630.   The test frequency was aligned to nominal zero-phase shift response of target batteries, and not aligned to any mains related processes.  At that time only a 12-bit ADC was practical, with software flipping the sensed signal polarity and averaging, to achieve a max error tolerance of <0.3% along with temperature errors mainly due to the 8038 and current sense resistor, for FS reading of 50 milliohm and resolution down to single micro-ohm level.  The AD630 was not the main concern for performance, but was a pricey part.
 

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6332
  • Country: ro
Re: Homebrew Lock-In Amplifier
« Reply #59 on: May 06, 2024, 05:41:35 am »
A few measures to lower the noise:

- keep the breadboard wires short and the loops short.  A circular shape is the largest area a string can enclose, and the larger the area of a loop, the more induced noise.  If the breadboarding wires are too long, then run them parallel, or twisted.  Preferably cut the wires just the right length, and run then as if it were a PCB (for example use single core wires stripped from a LAN patch cord, and cut them on the spot, just the right length, LAN wires are made of copper, and thick enough for the breadboard contacts).

Not so good (wires making loops with big area, components with long legs also making loops):


Better (wires were cut just the right length and routed straight, components terminals not very long):

Well, maybe the capacitors and resistors terminals could have been cut even shorter, like here:  https://www.eevblog.com/forum/projects/t20347/?action=dlattach;attach=1242762;image but you got the idea, avoid big area loops.

- avoid ground loops.  Use star-connection for GND wires, with a single GND point from where all the ground wires are leaving to different places in the circuit.  Do not chain the GND wires one after another.  A single GND point also separates the analog and digital GNDs.

- if the above is not possible in practice, at least keep the analog and digital current loops separated:
https://www.analog.com/media/en/training-seminars/tutorials/MT-031.pdf
https://www.analog.com/media/en/technical-documentation/application-notes/AN-202.pdf

- enclose the low signal stages in a grounded metal shield, shielding makes a big difference, it eliminates the noises induced from the outside of the circuit
« Last Edit: May 06, 2024, 06:38:35 am by RoGeorge »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #60 on: May 06, 2024, 11:10:21 am »
At risk of totally derailing the proceedings, I have an alternative suggestion: the Texas Instruments ADS1235. It has a 24 bit ADC, Programmable Gain Amplifier (PGA) and control logic for AC excitation. Seems like it can apply positive excitation, sample the ADC, apply negative excitation and sample the ADC again for you.
I am going to add an 18bit ADC to see if it improves the result over the 10bit ADC I am using now. Anyway the resolution improvement should be achieved thanks to the averaging of many measurements.


Back in the 90's I used an AD630 to extract a battery impedance measurement from industrial noisy live battery systems using a portable tool that applied about 0.1Arms sinewave into a battery (cell or string) and kelvin probe extracted a signal that was amplified and presented to the AD630.   An ICL8038 sine generator provided reasonable amplitude stability, muting, and a sync signal for the AD630.   The test frequency was aligned to nominal zero-phase shift response of target batteries, and not aligned to any mains related processes.  At that time only a 12-bit ADC was practical, with software flipping the sensed signal polarity and averaging, to achieve a max error tolerance of <0.3% along with temperature errors mainly due to the 8038 and current sense resistor, for FS reading of 50 milliohm and resolution down to single micro-ohm level.  The AD630 was not the main concern for performance, but was a pricey part.
I am going to increase the test current to check if with that change I can increase the resolution or if, on the contrary, the noise also goes up.


RoGeorge:
I will replace the most critical cables (analog part) with cables taken from a network cable, with the right size.
I used to always do the assemblies this way, but it's a lot of work and it doesn't always improve the result.
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #61 on: May 06, 2024, 05:58:15 pm »
With 10 times more current (R2=470 Ohm), I have achieved 10 times more resolution.
I now measure tens of microohms instead of hundreds of microohms.


Output measures:

Code: [Select]
21.499 mOhm
21.502 mOhm
21.496 mOhm
21.502 mOhm
21.504 mOhm
21.499 mOhm
21.508 mOhm
21.502 mOhm
21.500 mOhm
21.503 mOhm
21.508 mOhm
21.500 mOhm
21.505 mOhm
21.504 mOhm
21.507 mOhm
21.500 mOhm
21.512 mOhm
21.499 mOhm
21.505 mOhm
21.504 mOhm
21.505 mOhm
21.493 mOhm
21.496 mOhm
21.502 mOhm
21.499 mOhm
21.501 mOhm
21.502 mOhm
21.495 mOhm
21.492 mOhm
21.499 mOhm
21.486 mOhm
21.488 mOhm
21.499 mOhm
21.505 mOhm
21.494 mOhm
21.509 mOhm
21.501 mOhm
21.490 mOhm
21.501 mOhm
21.485 mOhm
21.492 mOhm
21.495 mOhm
21.493 mOhm
21.497 mOhm
21.492 mOhm
21.497 mOhm
21.501 mOhm
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #62 on: May 06, 2024, 06:14:07 pm »
If I reduce the gain of the amplifier (from 1050 to 105), the resolution is again worse.

DUT = 0.020 Ohm
Measure time = 1 second

Configuration 1:
R2 = 4700 Ohm
I_R2 = 1,04 mA
Amplifier gain = 1050
Output signal variation = 21 mV
Output signal noise = 20mVpp
Measures = 21.500 +- 0.1 mOhm

Configuration 2:
R2 = 470 Ohm
I_R2 = 10,4 mA
Amplifier gain = 1050
Output signal variation = 210 mV
Output signal noise = 20mVpp
Measures = 21.500 +- 0.01 mOhm

Configuration 3:
R2 = 470 Ohm
I_R2 = 10,4 mA
Amplifier gain = 105
Output signal variation = 21 mV
Output signal noise = 20mVpp
Measures = 21.500 +- 0.1 mOhm

EDIT:
The signal-to-noise ratio remains constant at approximately 50000 at full range (5Vpp in Vout).
« Last Edit: May 07, 2024, 10:49:06 am by Picuino »
 

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6332
  • Country: ro
Re: Homebrew Lock-In Amplifier
« Reply #63 on: May 06, 2024, 06:39:52 pm »
Nice!  :-+

Is this with the internal 10bit ADC of ATmega328, or with the 18bits ADC?

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 14319
  • Country: de
Re: Homebrew Lock-In Amplifier
« Reply #64 on: May 06, 2024, 07:02:28 pm »
The high gain of the amplifier also works as a kind of AA filter due to the limited bandwidth. So less gain can cause additional noise aliasing.
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #65 on: May 06, 2024, 07:33:19 pm »
Changing the measurement time (summation time) also affects the result.

New program without measure error due to an error in the first sample of the ADC:
Code: [Select]
/*
   Version 1.1 (06/05/2024)

   Copyright 2024 Picuino

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE.
*/

const int PIN_PWM_OUT = 3;
const int PIN_ANALOG = A6;
const int PIN_ANALOG_MUX = 6;

#define CLK_BOARD  16000000
#define UART_BAUDS  115200
#define TIMER0_PRESET 27        // Must be >= 27
#define TIMER0_FREQ  (CLK_BOARD / (((TIMER0_PRESET) + 1) * 64))  // 9250

const float MEASURE_TIME = 1;  // Seconds
const long SAMPLES_PER_LEVEL = 40;  // ADC samples per output level
const long LEVELS_PER_MEASURE = 2 * (long)((MEASURE_TIME * TIMER0_FREQ) / (SAMPLES_PER_LEVEL * 2));
const float BOARD_CALIBRATION = 8.0;  // Converts measure to milliohms

volatile long adc_sum;
volatile unsigned int adc_enable;
volatile unsigned int adc_value;
volatile unsigned long adc_samples;
volatile unsigned int level_state;
volatile unsigned char output_level;
volatile unsigned char output_level_old;
volatile unsigned char adc_measure_end;

char buff[50];
float resistance;

void setup() {
  Serial.begin(UART_BAUDS);
  Serial.print("MEASURE_TIME = "); Serial.println(1.0 * SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE / TIMER0_FREQ);
  Serial.print("SAMPLES_PER_LEVEL = "); Serial.println(SAMPLES_PER_LEVEL);
  Serial.print("LEVELS_PER_MEASURE = "); Serial.println(LEVELS_PER_MEASURE);


  // Set up output reference signal pin
  pinMode(PIN_PWM_OUT, OUTPUT);

  // Set up peripherals
  timer0_setup();
  adc_setup();

  // Main Loop
  adc_init();
  for (;;) {
    if (adc_measure_end == 1) {
      resistance = adc_sum;
      resistance *= (BOARD_CALIBRATION / (SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE));
      Serial.print(resistance, 2);
      Serial.println("\tmOhm");
      adc_init();
    }
  }
}

void loop() {}


void adc_init(void) {
  cli();
  PORTD |= (1 << PIN_PWM_OUT);
  output_level = 0;
  level_state = 0;
  delayMicroseconds(100);

  adc_sum = 0;
  adc_samples = 0;
  output_level = 0;
  adc_measure_end = 0;
  adc_enable = 1;
  sei();
}


void adc_setup(void) {
  analogRead(PIN_ANALOG);
  cli(); //stop interrupts

  ADMUX = (1 << 6) | (0 << ADLAR) | (PIN_ANALOG_MUX << 0);
  ADCSRA = (1 << ADEN) | (0 << ADSC) | (0 << ADATE) | (0 << ADIE) | (0b111);
  ADCSRB = 0x00;

  sei(); //allow interrupts
}


void timer0_setup(void) {
  cli(); //stop interrupts

  //set timer0 interrupt at 2kHz
  TCCR0A = 0; // set entire TCCR2A register to 0
  TCCR0B = 0; // same for TCCR2B
  TCNT0  = 0; //initialize counter value to 0
  // set compare match register
  OCR0A = TIMER0_PRESET;
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);

  sei(); //allow interrupts
}


ISR(TIMER0_COMPA_vect) {
  if (adc_enable) {

    // ADC Start Conversion
    ADCSRA |= (1 << ADSC);
    delayMicroseconds(12);  // Wait for Sample and Hold

    // Read and accumulate old ADC value
    if (adc_samples) {
      // Read ADC old measure
      adc_value = ADCW;

      // Add measure to accumulator
      if (output_level_old == 0) {
        adc_sum -= adc_value;
      }
      if (output_level_old == 1) {
        adc_sum += adc_value;
      }
    }
    adc_samples++;

    // Update next state
    level_state++;
    if (level_state >= (2 * SAMPLES_PER_LEVEL)) {
      level_state = 0;
    }

    // Update output reference signal
    output_level_old = output_level;
    if (level_state < SAMPLES_PER_LEVEL) {
      PORTD |= (1 << PIN_PWM_OUT);
      output_level = 0;
    }
    else {
      PORTD &= ~(1 << PIN_PWM_OUT);
      output_level = 1;
    }

    // Take one measure after several samples
    if (adc_samples > SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE) {
      PORTD |= (1 << PIN_PWM_OUT);
      output_level = 0;
      adc_measure_end = 1;
      adc_enable = 0;
    }
  }
}




Code: [Select]
MEASURE_TIME = 0.10
SAMPLES_PER_LEVEL = 40
LEVELS_PER_MEASURE = 22
22.04 mOhm
21.55 mOhm
21.41 mOhm
21.90 mOhm
21.52 mOhm
21.76 mOhm
22.08 mOhm
21.52 mOhm
21.89 mOhm
21.86 mOhm
21.71 mOhm
21.98 mOhm
21.60 mOhm
21.79 mOhm
22.03 mOhm
21.50 mOhm
21.98 mOhm
21.65 mOhm
21.57 mOhm
22.15 mOhm
21.48 mOhm
21.95 mOhm
21.99 mOhm
21.65 mOhm
21.74 mOhm
21.59 mOhm
21.81 mOhm
21.76 mOhm
22.13 mOhm
21.75 mOhm
21.70 mOhm
22.00 mOhm
21.54 mOhm
21.89 mOhm
21.90 mOhm
21.43 mOhm
22.26 mOhm
21.35 mOhm
21.89 mOhm
21.80 mOhm
21.38 mOhm
21.95 mOhm
21.79 mOhm
21.50 mOhm
21.99 mOhm
21.90 mOhm
21.33 mOhm
22.08 mOhm
21.33 mOhm
21.75 mOhm
21.58 mOhm
21.57 mOhm
21.78 mOhm
21.28 mOhm
21.67 mOhm
22.05 mOhm
21.76 mOhm
21.28 mOhm
21.86 mOhm
22.06 mOhm
21.40 mOhm
21.72 mOhm
21.83 mOhm
21.46 mOhm
22.15 mOhm
21.25 mOhm
21.77 mOhm



Code: [Select]
MEASURE_TIME = 0.99
SAMPLES_PER_LEVEL = 40
LEVELS_PER_MEASURE = 222
21.62 mOhm
21.68 mOhm
21.73 mOhm
21.62 mOhm
21.67 mOhm
21.64 mOhm
21.71 mOhm
21.66 mOhm
21.69 mOhm
21.68 mOhm
21.67 mOhm
21.69 mOhm
21.71 mOhm
21.74 mOhm
21.78 mOhm
21.64 mOhm
21.71 mOhm
21.72 mOhm
21.66 mOhm
21.59 mOhm
21.70 mOhm
21.71 mOhm
21.60 mOhm
21.68 mOhm
21.69 mOhm
21.68 mOhm
21.57 mOhm
21.63 mOhm
21.66 mOhm
21.64 mOhm
21.71 mOhm
21.74 mOhm
21.63 mOhm
21.76 mOhm
21.74 mOhm
21.74 mOhm
21.76 mOhm
21.70 mOhm
21.68 mOhm
21.65 mOhm
21.64 mOhm
21.67 mOhm
21.61 mOhm
21.52 mOhm
21.65 mOhm
21.64 mOhm
21.64 mOhm
21.67 mOhm
21.70 mOhm
21.53 mOhm
21.54 mOhm



Code: [Select]
MEASURE_TIME = 10.00
SAMPLES_PER_LEVEL = 40
LEVELS_PER_MEASURE = 2232
21.62 mOhm
21.59 mOhm
21.63 mOhm
21.60 mOhm
21.63 mOhm
21.67 mOhm
21.68 mOhm
21.72 mOhm
21.77 mOhm
21.72 mOhm
21.76 mOhm
21.75 mOhm
21.74 mOhm
21.61 mOhm
21.59 mOhm


EDIT:
Attached the schematic version 1.1 (with minor changes)
« Last Edit: May 06, 2024, 08:40:54 pm by Picuino »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #66 on: May 06, 2024, 07:35:13 pm »
Nice!  :-+

Is this with the internal 10bit ADC of ATmega328, or with the 18bits ADC?
All this measures still with the internal 10-bit ADC.
I wanted to do the tests before changing the results too much with the new ADC.
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #67 on: May 07, 2024, 01:17:32 pm »
Before continuing, I am going to measure with the multimeter (Agilent 34401A) the AC voltage (noise) at various points in the circuit to see if there is a way to reduce the amplifier's output noise.
First I keep the Arduino nano in an infinite loop without doing anything (cli(); for(;;);)

Voltages:
GND to GND (test probes in short circuit) = 5 uVAC
GND to VCC (main 5V supply) = 80 uVAC
GND to VCC (main 5V supply) = 2 uVAC  (without Arduino nano connected)

From now on I disconnect the Arduino nano from the 5V power supply and only power it via USB so that it does not produce so much noise on the power supply.

R5-R6 voltage divisor = 3 uVAC
LMV358 output pin 1 ( 2.5Vref ) = 1 uVAC
DUT pin + = 4 uVAC
DUT pin - = 4 uVAC
Instrumentation amplifier output = 12000 uVAC   (Gain = 1050)
Instrumentation amplifier output =     540 uVAC   (Gain = 105)

Instrumentation amplifier Vcc = 5 uVAC
Instrumentation amplifier Vcc = 4 uVAC
« Last Edit: May 07, 2024, 01:24:02 pm by Picuino »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #68 on: May 07, 2024, 01:23:07 pm »
I have changed the position of the gain resistor so that it is as close to the circuit as possible.
Now the noise has decreased quite a bit.


Instrumentation amplifier output = 2200 uVAC   (Gain = 1050)
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #69 on: May 07, 2024, 01:29:26 pm »
The measures shows lower noise:

Code: [Select]
MEASURE_TIME = 0.99
SAMPLES_PER_LEVEL = 40
LEVELS_PER_MEASURE = 222
21.09 mOhm
21.10 mOhm
21.06 mOhm
20.98 mOhm
20.99 mOhm
20.98 mOhm
21.11 mOhm
21.00 mOhm
21.01 mOhm
21.00 mOhm
21.03 mOhm
21.04 mOhm
21.06 mOhm
20.99 mOhm
21.04 mOhm
20.98 mOhm
21.00 mOhm
21.11 mOhm
21.06 mOhm
21.04 mOhm
20.99 mOhm
21.02 mOhm
21.06 mOhm
21.07 mOhm
21.04 mOhm
21.10 mOhm
21.08 mOhm
21.10 mOhm
20.98 mOhm
21.12 mOhm
21.02 mOhm
21.08 mOhm

EDIT:
Conclusion: the position of the gain resistor R1 is critical. Keep the resistor as close as possible to the integrated and free of noise.
« Last Edit: May 07, 2024, 01:35:39 pm by Picuino »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #70 on: May 07, 2024, 03:30:09 pm »
I have added a quadrature accumulator to be able to make Impedance measurements.
To achieve this I had to make some changes in the schematic:
 * I have eliminated the transistor (it seems that the noise was not coming from there).
 * I have added capacitive coupling to the instrumentation amplifier.

New program:
Code: [Select]
/*
   Version 2.0 (07/05/2024)

   Copyright 2024 Picuino

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE.
*/

const int PIN_PWM_OUT = 3;
const int PIN_ANALOG = A6;
const int PIN_ANALOG_MUX = 6;

#define CLK_BOARD  16000000
#define UART_BAUDS  115200
#define TIMER0_PRESET 27        // Must be >= 27
#define TIMER0_FREQ  (CLK_BOARD / (((TIMER0_PRESET) + 1) * 64))  // 9250

const float MEASURE_TIME = 1;  // Seconds
const long SAMPLES_PER_LEVEL = 6;  // ADC samples per output level. must be an even number.
const long LEVELS_PER_MEASURE = 2 * (long)(((MEASURE_TIME) * (TIMER0_FREQ)) / (2 * SAMPLES_PER_LEVEL));
const float BOARD_CALIBRATION = 8.0;  // Converts measure to milliohms

volatile long adc_acc_inphase;
volatile long adc_acc_quadrature;
volatile unsigned char adc_enable;
volatile unsigned char adc_measure_end;
volatile unsigned int adc_value;
volatile unsigned long adc_samples;
volatile unsigned int level_state;
volatile unsigned char level_state_old;

float resistance_inphase;
float resistance_quadrature;

void setup() {
  Serial.begin(UART_BAUDS);

  Serial.print("MEASURE_TIME = ");
  Serial.print(1.0 * (SAMPLES_PER_LEVEL) * (LEVELS_PER_MEASURE) / (TIMER0_FREQ));
  Serial.println(" s");

  Serial.print("MEASURE_FREQUECY = ");
  Serial.print((TIMER0_FREQ) / (2.0 * SAMPLES_PER_LEVEL));
  Serial.println(" Hz");

  Serial.print("SAMPLES_PER_LEVEL = ");
  Serial.print(SAMPLES_PER_LEVEL);
  Serial.println(" Samples");


  // Set up output reference signal pin
  pinMode(PIN_PWM_OUT, OUTPUT);

  // Set up peripherals
  timer0_setup();
  adc_setup();

  // Main Loop
  adc_init();
  for (;;) {
    if (adc_measure_end == 1) {
      resistance_inphase = adc_acc_inphase;
      resistance_quadrature = adc_acc_quadrature;
      resistance_inphase *= (BOARD_CALIBRATION / (SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE));
      resistance_quadrature *= (BOARD_CALIBRATION / (SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE));
      Serial.print(resistance_inphase, 2);
      Serial.print("\tmOhm R  \t");
      Serial.print(resistance_quadrature, 2);
      Serial.println("\tmOhm Zc");
      adc_init();
    }
  }
}

void loop() {}


void adc_init(void) {
  cli();
  PORTD |= (1 << PIN_PWM_OUT);
  level_state = 0;
  delayMicroseconds(100);

  adc_acc_inphase = 0;
  adc_acc_quadrature = 0;
  adc_samples = 0;
  adc_measure_end = 0;
  adc_enable = 1;
  sei();
}


void adc_setup(void) {
  analogRead(PIN_ANALOG);
  cli(); //stop interrupts

  ADMUX = (1 << 6) | (0 << ADLAR) | (PIN_ANALOG_MUX << 0);
  ADCSRA = (1 << ADEN) | (0 << ADSC) | (0 << ADATE) | (0 << ADIE) | (0b111);
  ADCSRB = 0x00;

  sei(); //allow interrupts
}


void timer0_setup(void) {
  cli(); //stop interrupts

  //set timer0 interrupt at 2kHz
  TCCR0A = 0; // set entire TCCR2A register to 0
  TCCR0B = 0; // same for TCCR2B
  TCNT0  = 0; //initialize counter value to 0
  // set compare match register
  OCR0A = TIMER0_PRESET;
  // turn on CTC mode
  TCCR0A |= (1 << WGM01);
  // Set CS01 and CS00 bits for 64 prescaler
  TCCR0B |= (1 << CS01) | (1 << CS00);
  // enable timer compare interrupt
  TIMSK0 |= (1 << OCIE0A);

  sei(); //allow interrupts
}


ISR(TIMER0_COMPA_vect) {
  if (adc_enable) {

    // ADC Start Conversion
    ADCSRA |= (1 << ADSC);
    delayMicroseconds(12);  // Wait for Sample and Hold

    // Read and accumulate old ADC value
    if (adc_samples) {
      // Read ADC old measure
      adc_value = ADCW;

      // Add measure to accumulator
      if (level_state_old < SAMPLES_PER_LEVEL) {
        adc_acc_inphase -= adc_value;
      }
      else {
        adc_acc_inphase += adc_value;
      }
      if ((level_state_old >=  0.5 * SAMPLES_PER_LEVEL) && (level_state_old < 1.5 * SAMPLES_PER_LEVEL)) {
        adc_acc_quadrature -= adc_value;
      }
      else {
        adc_acc_quadrature += adc_value;
      }
    }

    adc_samples++;

    // Update next state
    level_state_old = level_state;
    level_state++;
    if (level_state >= (2 * SAMPLES_PER_LEVEL)) {
      level_state = 0;
    }

    // Update output reference signal
    if (level_state < SAMPLES_PER_LEVEL) {
      PORTD |= (1 << PIN_PWM_OUT);
    }
    else {
      PORTD &= ~(1 << PIN_PWM_OUT);
    }

    // Take one measure after several samples
    if (adc_samples > SAMPLES_PER_LEVEL * LEVELS_PER_MEASURE) {
      adc_measure_end = 1;
      adc_enable = 0;
    }
  }
}



Measurements of a one-turn coiled cable with a diameter of 5 cm:
Code: [Select]
MEASURE_TIME = 1.00 s
MEASURE_FREQUECY = 744.00 Hz
SAMPLES_PER_LEVEL = 6 Samples
485.72 mOhm R  -0.18 mOhm Zc
485.68 mOhm R  -0.26 mOhm Zc
485.81 mOhm R  -0.21 mOhm Zc
486.26 mOhm R  -0.21 mOhm Zc
486.30 mOhm R  -0.29 mOhm Zc
486.78 mOhm R  -0.19 mOhm Zc
487.34 mOhm R  -0.22 mOhm Zc
487.63 mOhm R  -0.28 mOhm Zc
487.70 mOhm R  -0.27 mOhm Zc
487.77 mOhm R  -0.31 mOhm Zc
487.68 mOhm R  -0.25 mOhm Zc
487.91 mOhm R  -0.29 mOhm Zc
487.96 mOhm R  -0.28 mOhm Zc
488.01 mOhm R  -0.24 mOhm Zc
487.99 mOhm R  -0.24 mOhm Zc
487.98 mOhm R  -0.25 mOhm Zc
488.02 mOhm R  -0.20 mOhm Zc
488.25 mOhm R  -0.25 mOhm Zc
488.23 mOhm R  -0.24 mOhm Zc
488.44 mOhm R  -0.31 mOhm Zc
488.39 mOhm R  -0.28 mOhm Zc
488.41 mOhm R  -0.26 mOhm Zc
488.35 mOhm R  -0.32 mOhm Zc


Measurements of a capacitor (1000uF, 16V):
Code: [Select]
MEASURE_TIME = 1.00 s
MEASURE_FREQUECY = 744.00 Hz
SAMPLES_PER_LEVEL = 6 Samples
88.13 mOhm R  130.24 mOhm Zc
108.88 mOhm R  161.26 mOhm Zc
108.79 mOhm R  161.40 mOhm Zc
108.70 mOhm R  161.41 mOhm Zc
108.73 mOhm R  161.39 mOhm Zc
108.71 mOhm R  161.38 mOhm Zc
108.87 mOhm R  161.48 mOhm Zc
108.74 mOhm R  161.47 mOhm Zc
108.74 mOhm R  161.47 mOhm Zc
108.75 mOhm R  161.36 mOhm Zc
108.76 mOhm R  161.42 mOhm Zc
108.75 mOhm R  161.52 mOhm Zc
108.78 mOhm R  161.48 mOhm Zc
108.83 mOhm R  161.53 mOhm Zc
108.76 mOhm R  161.51 mOhm Zc
108.80 mOhm R  161.44 mOhm Zc
108.83 mOhm R  161.44 mOhm Zc
108.69 mOhm R  161.44 mOhm Zc
108.88 mOhm R  161.45 mOhm Zc
108.81 mOhm R  161.49 mOhm Zc
108.91 mOhm R  161.40 mOhm Zc
108.92 mOhm R  161.49 mOhm Zc
108.89 mOhm R  161.64 mOhm Zc
108.92 mOhm R  161.53 mOhm Zc
109.02 mOhm R  161.53 mOhm Zc
108.91 mOhm R  161.44 mOhm Zc
108.93 mOhm R  161.55 mOhm Zc
108.95 mOhm R  161.55 mOhm Zc
108.94 mOhm R  161.56 mOhm Zc
108.99 mOhm R  161.63 mOhm Zc


« Last Edit: May 07, 2024, 08:23:07 pm by Picuino »
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #71 on: May 07, 2024, 03:56:02 pm »
Attached:
Signal in instrumentation amplifier output measuring a capacitor of 1000uF.
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #72 on: May 08, 2024, 09:42:36 am »
I am trying to start up the ADC AD7691, but it just makes strange measurements.
With both inputs connected to 2.5V it should measure zero, but it makes full scale measurements:

Code: [Select]
65534
65533
65532
65533
65534
65532
65533
65533
65533
65532
65534
65532
65532
65533
65534
65532
65533
65533
65533
65533
65533
65533
65534
65532
65533
I am only getting the first 16 bits, discarding the last two bits.

« Last Edit: May 08, 2024, 09:45:39 am by Picuino »
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 14319
  • Country: de
Re: Homebrew Lock-In Amplifier
« Reply #73 on: May 08, 2024, 09:50:27 am »
The input range of the AD7691 is +-Vref. So the result should be signed and not an unsigned number. So the results looks like there is a slight negative offset of some 3 LSBs, which looks reasonable.
 

Offline PicuinoTopic starter

  • Frequent Contributor
  • **
  • Posts: 932
  • Country: 00
    • Picuino web
Re: Homebrew Lock-In Amplifier
« Reply #74 on: May 08, 2024, 10:13:49 am »
 :-+
Yes, I have changed the negative input by connecting it to ground and the readings are correct.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf