ATtiny85 Inductance Meter

by Poldo in Circuits > Arduino

2059 Views, 3 Favorites, 0 Comments

ATtiny85 Inductance Meter

Cover Photo2.jpg

To start off I was inspired by a similar project I saw on an Electronoobs YouTube video. It was really simple and well presented as usual. It demonstrates the theory of how we will go about measuring the inductance. Please have a look if you havn't seen it.

Inductance meter with Arduino - YouTube

My approach is similar as I am pulsing a resonant tank circuit and measuring the period of the resulting sinewave however, there are some key differences. This inductance meter uses an ATtiny85 microcontroller and the internal comparator which replaces the external LM339 comparator. An emitter-follower transistor is added to provide more drive current to the resonant tank circuit allowing for inductance measurements down to 1uH. Instead of using the pulsIn() function, I use Timer0 along with Timer0 interrupt and the internal interrupt from the comparator to create a precision frequency counter that results in more accurate inductor measurment. Finally, a 0.96 OLED display replaces the larger LCD for a more compact design.

Supplies

U1 - Voltage Regulator - 7805 or equiv

U2 - Microcontroller - ATtiny85

M1 - OLED Display - SSD1306 or equiv I2C display

Q1 - Transistor NPN - 2N2222 (or equivalent general purpose switching transistor)

C1, C2, C7, C8 - Capacitor 0.1uF (Ceramic)

C3, C4 - Capacitor - 1uF (Polarized Electrolytic type)

C5 - Capacitor - 10uF (Non-Polarized Ceramic or Film type) [This project uses Tayio Yuden P/N UMK325BJ106M]

C6 - Capacitor - 1000uF (Polarized Electrolytic type)

D1 - Diode - 1N4001 (or equiv)

R1 - Potentometer - 10kΩ (multi-turn is preferred for better precision) [This project uses a Bourns 3296 W103]

R2 - Resistor - 1kΩ

R3 - Resistor - 3.3Ω

R4 - Resistor - 330Ω

Schematic

ATtiny85 schematic.png

U1, C1 and C3 form the input to the voltage regulator that provides a stable 5V from a 9 - 15V input. C2 and C4 provide DC filtering for the +5V line and C7 and C8 are high frequency bypass capacitors. When an inductor under test is connected, R1 and R2 form a voltage divider. For initial calibration, R1 is adjusted to 1volt measured at PB1 which is connected to the inverting input of the internal comparator and this is just below the 1.1volt internal reference applied to the non-inverting input of the comparator. A 45us pulse from U2 PB4 is applied to the base of Q1 through R4. Q1 provides a current pulse of several hunderd milliamps from C6 through R3 and D1 to the resonant tank circuit C5 and the inductor under test. Diode D1 prevents the sinewave voltage accross the resonant tank circuit from feeding back to the source when the drive pulse signal is at 0volts. The sinewave generated from the resonant tank circuit is fed to PB1 through R2 and swings above and below 1volt.

Breadboard

ATtiny Breadboard Rev2_bb.png

With an inductor connected apply power and adjust R1 to 1.0volts at PB1 input of the ATtiny85. Note: While the volt meter is connected the inductance and frequency values on the OLED display will be unstabe due to the noise introduced by the volt meter. Don't forget to disconnect it.

How It Works

ATtiny Block Diagram.png

The basic concept and the theory of how we can obtain an inductance measurement is well illustrated in the Electronoobs video I mentioned in the introduction including some very good animation. Basically, if you provide an electrical pulse of energy to an inductor and capacitor connected in parallel which is commonly referred to as a tank circuit, a sinewave voltage will be produced at the tank circuit's resonant frequency. This resonant frequency can be expressed as;

1 / (2π √L*C)

where:

L = inductance in Henrys

C = capacitance in Farads

Since we know the capacitance which in our case will be 10uF and when we pulse the tank circuit we will measure the resonant frequency and we can then use the following formula to solve for inductance;

L = 1 / (4π2 * f2 * C)

where;

L = inductance in Henrys

f = frequency we measure in Hz

C = .00001F (10uF)

Visit this handy online calculator showing the various formulas;

Resonant Frequency Calculator | LC Calculator

Ok, so now we know that we need to pulse the unknown Inductor connected in parallel with a known capacitor which will result in a resonant frequency that we can measure in order to calculate our inductance value.

Refer to the the block diagram shown above and the following pseudo code explanation to see how we go about implementing the inductance meter. Lets start with Timer0. The ATtiny85 is running at 16MHz internal clock and the prescaler is set to divide by 8. Timer0 is setup to free run in CTC mode and counts to the value of the OCRA register. In our case the value is set to 100. Therefore, the frequency of Timer0 = 16Mhz / 8 / 100 = 20kHz. An Interrupt Service Routine(ISR) is triggered when the counter reaches the OCRA value of 100. Since the period of 20kHz = 1 / 20,000 the ISR is triggered every 50us. There is a software counter called TIMR_50us_cntr and it gets incremented during each Timer0 ISR and the TCNT0 register is being incremented every 0.5us [1 / (16MHz / 8)]. Both the TIMR_50us_cntr and the TNCT0 counter register form the basis of our frequncy counter as will be shown later.

Next, lets talk about the comparator. A comparator consists of one positive(+) input terminal , one negative(-) input terminal and one output terminal. It does exactly what its name suggests as it compares two input voltages. If the +input is higher in voltage than the -input the output will be HIGH. Conversley, if the -input is higher in voltage than the +input the output will be LOW. One key feature of a comparator is it's very high gain meaning a very small difference between voltages at the +/- inputs (say a few milli-volts) will cause the output to swing HIGH or LOW. Use following link to learn more about comparators and how to use them.

Op amp comparator tutorial and clapper circuit - Afrotechmods

As shown in the block diagram the ATtiny85 internal comparator will have its +input connected to an internal 1.1 volt reference. The -input is connected to port PB1. The resonant circuit output sinewave voltage is fed to PB1 and is biased around 1 volt. This means that the resonant sinewave will swing above and below adding or subtracting from the 1 volt bias point. When the AC sinewave is at 0volts, 1 volt is applied to the -input and since the +input is at 1.1 volts the output of the comparator will be HIGH. The Comparator is configured to execute an ISR on the falling edge of the comparator's output. As soon as the resonant sinewave goes positive greater than 1.1 volts, the - input is now greater than the +1.1 volts at the +input so the output of the comparator will go LOW. This will trigger the comparator ISR and at this instant the trigger_cnt = 0 and will be incremented to 1 on the next ISR. In addition both the TIMR_50us_cntr and the TNCT0 counter are set to zero as this is the beginning of our sinewave period to be measured. In addition, PB3 is a monitor output which is now set HIGH. As the sinewave continues and falls below 1.1volts half way through its cycle, the comparator output will go HIGH. When the sinewave completes one full cycle it again goes positive greater than 1.1 volts so the output of the comparator will go LOW. This will trigger the comparator ISR again and at this instant the trigger_cnt = 1 and both the TIMR_50us_cntr and the TNCT0 counter values will be stored in variables counter_1 and counter_2 respectively and the PB3 monitor output is set LOW. Now that we have our counter_1 and counter_2 values lets see how to go about calculating the frequency of our resonant sinewave period.

In the forever Loop() is the Calculate / Display section. Once every second, the inductance will be calculated and displayed on the OLED display. The TIMR_50us_cntr is used to count up to one second (50us * 20000). The first thing is to add together our counter_1 and counter_2 values to obtain a total count in microseconds. The variable count_us will hold this value. Since counter_1 contains the # of 50us increments we will need to convert it to TCNT0 increments by multiplying by 100 we can now add in the counter_2 value which is the TCNT0 register value. We now have the total # of TCNT0 increments. In order to get the total # of microsconds we need to take this total which is in 0.5us increments and divide by 2. Therefore the equation is;

count_us = ((count_1 * 100) + count_2)/2

This is now the period in microseconds of one cycle of the resonant sinewave. We can now calculate our resonant sinewave frequency as;

frequency(Hz) = 1 / (count_us * 1E-6)

Since our known capacitance = 10uF or 10E-6F we can now calculate the inductance as;

L = 1 / (4π2 f2 C) 

where;

L = inductance in Henrys

f = our calculated frequency in Hz

C = 10E-6 (Farads)

We can now convert our inductance value in Henrys to either mH or uH based on the following criteria. If the inductance value is greater than or equal to 1E-3 then multiply the inductance value by 1E+3 to get mH else multiply the inductance value by 1E+6 to get uH. Now we can send the frequency value and inductance value out to our OLED display. Next we need to reset all our counters to zero;

TIMR_50us_cntr = 0;

TCNT0 = 0;

count_1 = 0;

count_2 = 0;

The last thing to do before leaving the Calculate / Display section is to call our pulse() function so we can generate a new pulse and start the whole cycle again.

When the pulse() function is called, the TCNT0 register is set to zero. While TCNT0 is less than 90(45us) the comparator ISR is disabled and PB4 is set HIGH. The ISR is disabled at this point because the initial rise of the pulse will cause a sinewave to be generated in the resonant tank circuit and we dont want the comparator to trigger until after the pulse goes LOW. Once TCNT0 is greater than or equal to 90(45us) PB4 is set LOW and the comparator ISR is enabled and trigger_cnt is reset to zero.

Test Exhibit

Cover Photo2.jpg
comparator Output 6849Hz.jpg
pulsed sinewave.jpg

The inductance meter is displaying the values of frequency and Inductance while the first oscilloscope image Top-Right shows the 45us pulse at PB4 and the comparator output PB3. The second oscilloscope image Bottom-Right is the same except CH2 is switched fromPB3 to PB1 in order to see the resonant sinewave. Notice the frequency counter on the oscilloscope matches the inductance meter's frequency.

Calibration and Accuracy

Coil32.png
1.1uH.jpg
1.1uH_2.jpg

The range of inductance that can be measured should be from 1.0uH to 999mH and higher. The calibration that was done by setting PB1 to 1.0volts using R1 is an initial calibration. It should result in stable accurate readings down to a few microhenrys. However to read accurately down to 1.0uH a finer adjustment of R1 is necessary.

First you will need to create an accurate inductor close to 1.0uH. This can be easily accomplished by winding an air core inductor of just a few turns using the Coil32 calculator.

One-layer inductance coil calculator - Coil32

In the Coil32 example image above, I set the units to mil/inch used AWG#17 wire (It's stiff and I had some laying around) If you have wire and are not sure of the guage you can measure the diameter with a calipers and then uncheck the Automatic tic box in the Coil32 calculator and manually put in the wire diameter size. Keeping L as close to 1.0uH as possible play around with the coil former diameter and your wire size in order to get an even number of turns. In my example 9Turns on a 0.6" diameter gives an inductance of 1.1uH. The coil length is not that critical just get it close.

In terms of accuracy, the higher the inductance value the the more accurate the reading. Since the variable count_us is an integer value the resonant sinewave period measurement has a resolution of +/-1us. Doing the math which I woun't go into here, the following measurement tolerances apply:

1.0uH +/-5% (limited to +/-10% by display resolution of +/-.1uH)

2.0uH +/-2.5% (limited to +/-5% by display resolution of +/-.1uH)

10uH +/- 2%

25uH +/-1%

2.5mH +/-0.1%

Calibration Exhibit

1.1uH Breadboard.jpg
comparator Output 47620Hz.jpg

Initially, when I connected the inductor it measured 1.5uH. I slowly and carefully adjusted the pot in the direction of closer to 1.1V and the value began to decrease to a final value of 1.1uH. After adjusting I measured the voltage at PB1 to be 1.04volts. This is why a multi-turn pot is desireable as opposed to a single turn pot. Comparing the frequency reading on the oscilloscope to the inductance meter frequency demonstrates the accuracy and stabilty of this design.

The Code

/*
  I was inspired by a similar project I saw on an Electronoobs YouTube video. It was really simple and well presented as usual.
  It demonstrates the theory of how we will go about measuring the inductance. Please have a look if you havn't seen it.
  https://www.youtube.com/watch?v=BGjV5vUvGPc&t=472s
  http://electronoobs.com/images/Arduino/tut_10/339_circuit.png


  My approach is similar as I am pulsing a resonant tank circuit and measuring the period of the resulting sinewave however,
  there are some key differences. This inductance meter uses an ATtiny85 microcontroller and the internal comparator which replaces the external LM339 comparator.
  An emitter-follower transistor is added to provide more drive current to the resonant tank circuit allowing for inductance measurements down to 1uH.
  Instead of using the pulsIn() function, I use Timer0 along with internal interrupt from the comparator creating a frequency counter that results
  in more accurate inductor measurment. Finally, a 0.96 OLED display replaces the larger LCD for a more compact design.
  
*/


// Use the Adafruit's TinyWireM library:
#include <TinyWireM.h>
#include <Tiny4kOLED.h>


unsigned int count_1;// 50us_count (0 - 65535) 0 - 3.27sec in 50us increments
int count_2;// TCNT0 Count (0-99) 0 - 50us in 0.5us increments
unsigned int count_us;// micro-seconds
unsigned int TIMR_50us_cntr;// Timer0 ISR counter
unsigned int period = 20000;// 20000 * 50us = 1sec) Display and Pulse interval
int trigger_cnt;// # of comparator output triggers detected
bool mH = false;// Change units between mH and uH


void setup() {
  oled.begin(128, 64, sizeof(tiny4koled_init_128x64br), tiny4koled_init_128x64br);
  oled.on();
  oled.clear();
  oled.setFont(FONT8X16P);
  oled.setCursor(0, 0);
  oled.print("  Inductance   ");
  oled.setCursor(0, 2);
  oled.print("     Meter     ");
 
  sei();// Enable Global Interrupts
  
  pinMode(4, OUTPUT);// Drive Pulse on PB4
  pinMode(3, OUTPUT);// Comparator output pulse on PB3


  // ATtiny85 is running on 16MHz Internal
  // Timer0 is used as the master timer for generating the puse, display handleing and a precision frequency counter
  // It operates in "CTC Mode" and counts from 0-100 and rolls over again and each increment is 500ns
  // an interrupt is generated at OCRA
  /*
    TCCR0A - Timer/Counter0 Control Register (page 79)
    COM0A1=0 COM0A0=0 COM0B1=0 COM0B0=0 (Normal port operation, OC0A/OC0B disconnected)
    WGM01=1 WGM00=0 (Sets CTC Mode along with WGM02 = 0 in the TCCR0B Register)
  */
  TCCR0A = B00000010;
  /*
    WGM02=0 (Sets CTC Mode along with WGM00 and WGM01 in the TCCR0A Register)
    CS02=0 CS01=1 CS00=0 (Divide by 8)
  */
  TCCR0B = B00000010;
  /*
    TIMSK -Timer/Counter Interrupt Mask Register
    OCIE0A=1(Timer/Counter1 Compare MatchA, interrupt is enabled)
  */
  TIMSK = B00010000;
  /*
    //TIFR – Timer/Counter Interrupt Flag Register
    //OCF0A=1(When Interrups are enabled and  OCIE1A=1 OCF1A=1 the Timer/Counter1 A compare match interrupt is executed.
  */
  TIFR = B00010000;


  OCR0A = 100;// 50us ISR
  
  // Internal Comparator
  // The internal comparator is used to detect the rising edge of a sinewave generated by the resonant tank circuit excited by a pulse
  // When Bit6 is written logic zero, AIN1 is applied to the negative input of the Analog Comparator. (PB1)
  ADCSRB &= B10111111;
  /*
    ACSR – Analog Comparator Control and Status Register (page 120)
    Comparator is using the internal 1.1V Bandgap reference applied to the AIN0 input and interrupt is enabled.
    It is compared with input AIN1 on PB1 and set to trigger the interrupt on the falling edge.
  */
  ACSR |= B01011010; // 
      //   ||||||||_ ACIS0: Analog Comparator Interrupt Mode Select works together with ACIS1
      //   |||||||__ ACIS1:ACIS0 10= falling edge; 11= raising edge; 00= toggle. Note this refers to the output of the internal comparator NOT the input signal at PB1
      //   ||||||___ Reserved
      //   |||||____ ACIE  Interrupt Enable
      //   ||||_____ ACI   Analog Comparator Interrupt Flag (R/W), write 1 to clear it.
      //   |||______ ACO   Analog Comparator Output (read only)
      //   ||_______ ACBG  1= Bandgap Select (1.1V); 0= compare with AIN0 (pin 5)
      //   |________ ACD   Analog Comparator Disable


}


// 16Mhz / 8 = 0.5us per count
// 16Mhz / 8 / 100 = 20kHz = TIMR_50us ISR
ISR(TIMER0_COMPA_vect)
{
  //Each increment of this counter represents 100 0.5us counts
  TIMR_50us_cntr++;
}


// Comparator Output triggers on each rising edge of the sinewave. Note: The ACSR register bits ACIS1:ACIS0 are set to detect the falling edge since
// this refers to the output of the internal comparator that triggers the ISR and NOT the input signal on AIN1 (PB1) (page 120 / 121 of the spec)
ISR (ANA_COMP_vect)
{
  // Capture the first two rising edges (360º sinewave)and create a single positive pulse on output D3
  if(trigger_cnt == 0)
  {
    //Set PB3 HIGH
    PORTB |= B00001000;
    // Reset counters    
    TCNT0 = 0;
    TIMR_50us_cntr = 0;
  }
  if(trigger_cnt == 1)
  {
    //Set PB3 LOW
    PORTB &= B11110111;
    // Get timer values
    count_1 = TIMR_50us_cntr;
    count_2 = TCNT0;
  } 
  trigger_cnt++;
}


void pulse() {
  // Use Timer0 to create a 50us Pulse
  // Reset Timer0 to zero
  TCNT0 = 0;


  // While Timer0 < 45us
  while(TCNT0 < 90)
  {
    // Disable Comparator Interrupt
    ACSR &= B11110111;
    //Set PB3 LOW
    PORTB &= B11110111;
    digitalWrite(4, HIGH);
  }
  digitalWrite(4, LOW);
  // Enable Comparitor Interrupt
  ACSR |= B00001000;
  trigger_cnt = 0;  
}


void loop() {
    // Calculate and display once every second
    // 50us * 20000 = 1sec
    if(TIMR_50us_cntr >= period)
    {
      // Each counting increment of Timer0 TCNT0 is 0.5us
      // count_1 = TIMR_50us_cntr meaning that every count_1 increment contains 100 Timer0 increments(50us)
      // count_2 = TCNT0 count which is 0.5us increments
      // So by adding the two counts and dividing by 2 we get count_us which is the total # of micro-seconds 
      count_us = ((count_1 * 100) + count_2)/2;


      //This is our known capacitance value
      double capacitance = 10E-6;
      // Calculate the frequency
      unsigned long freq = 1 / (count_us * 1E-6);//
      // The formula to find inductance when capacitance and frequency are known is  L = 1 / (4π^2 f^2 C)   Inductance [H]
      double inductance = 1./(capacitance*pow(freq,2)*4.*pow(M_PI,2));//
      if(inductance >= 1E-3)
      {
        mH = true;
        inductance *= 1E3;
      }
      else
      {
        mH = false;
        inductance *= 1E6;
      }
      oled.setCursor(0,4);
      oled.print("                ");      
      oled.setCursor(0,4);
      oled.print(freq);
      oled.setCursor(64,4);
      oled.print("Hz");
      oled.setCursor(0,6);
      oled.print("                ");          
      oled.setCursor(0,6);
      oled.print(inductance,1);
      oled.setCursor(64,6);
      if(mH)
        oled.print("mH");
      else
        oled.print("uH");
        
      //Reset all counters to zero
      TIMR_50us_cntr = 0;
      TCNT0 = 0;
      count_1 = 0;
      count_2 = 0;


      // Start a new pulse
      pulse(); 
    }
}

Programming the ATtiny85

Arduino_IDE.png

For this project the ATtiny85 needs to run at an internal clock speed of 16MHz. Since the default factory setting is 1MHz before loading the program you will need to set the Arduino to Internal 16MHz and "Burn Bootloader" when complete go ahead and upload the code normally.

For those of you who are not familiar with how to program an ATtiny you can acomplish it with either an Arduino Uno or an Arduino Nano. There is an Instructable on the topic which I havn't tried but looks to be good. The one I used was the "How to Program an ATtiny with Arduino Nano - YouTube" by Tanner Tech.

How to Program an ATtiny with Arduino Nano - YouTube

How to Program an Attiny85 From an Arduino Uno : 7 Steps

Drop me a comment and let me know what you think.

Cheers!