Let’s make an Universal Semiconductor and electronic component tester using Arduino. We use many electronic components in our projects and different products. Sometimes, you may need a simple electronic component tester that can help you testing some components for you. Here in this article, we will learn to make an Electronic Component Tester using Arduino, sorry, using Atmega328p MCU directly. But we’ll use Arduino IDE for programming. So let’s start!
⚠️Disclaimer:
Working with electricity involves serious risk. Ensure you have the necessary skills and take proper safety precautions before attempting any electrical projects. Proceed at your own risk — the author assumes no responsibility for any damage, injury, or issues resulting from the use or misuse of the information provided.
All content on this website is original and protected by copyright. Please do not copy or reproduce content without permission. While most of the resources shared here are open-source and freely accessible for your learning and benefit, your respect for our intellectual effort is appreciated.
If you find our tutorials helpful, consider supporting us by purchasing related materials or sharing our work — it helps keep the content flowing.
Need help or have questions? Leave a comment below — the author is always happy to assist!
Table of Contents
About different components:
There are many types of electronic components available, still, some are on the way to invention. But there are some very basic components we use the most like resistors, capacitors, inductors, transistors, etc. There is no such limitation of values for these category devices. With all, there is actually a vast number of different components. Sometimes it is very helpful if there is a testing device that can help to test some of these components for you.
So, let’s make an electronic device tester for ourselves.
Circuit diagram:
You may find this useful: How to remove noise/garbage from the HD44780 LCD display
You may find this helpful too: Arduino Bootloader with ICSP and zip socket
PCB:
Arduino Code:
Note: The original code was written by someone found in instructables.com and I just modified some parts and designed a PCB. The code is very long and was not written once. Day by day it is this far. So the credit goes to someone who wrote this at the first and them who modified it later. I also modified some. Maybe someone next will update more. #modification.
Read more:
Connect to Raspberry Pi from your Laptop/Desktop using VNC Viewer
How to reduce noise from DC motor
Interfacing External EEPROM with PIC microcontroller
Not Enough ROM/RAM error with micro-controllers
Read ThingSpeak Channel using ESP8266 and Arduino
Caller ID detection using Arduino
Simple voltage protector for your appliances
DC/DC converter using Transistors
Simple Component Detector using PIC16F877A
Voltage Stabilizer Circuit with 4 Relays
6V Lead-Acid battery charger circuit
5 coolest multimeters you can buy
Top 5 bench power supplies you can buy
Top 5 handheld oscilloscopes you can try
Top 5 Digital Multimeters for beginners
Digital Clock with DS3231 & PIC microcontroller
Digital Clock with PIC16F676 & Seven Segment Display
Digital Clock using a PIC microcontroller & RTC DS1307
How to remove noise/garbage from the HD44780 LCD display
How to design a voltage stabilizer using a micro-controller from A to Z
Capacitor Power Supply parts calculator
Solar Charge Controller circuit & working principle of ON/OFF charge controller
A smart Battery charger circuit design guide
#define BUTTON_INST //Button Installed
#define LCD_PRINT //Print on LCD
#define DET_COMP_ANALYSIS //Detailed Component Analysis (Soon)
#define TIMEOUT_BL 600 //LCD Backlight Timeout
#define LONG_PRESS 26 //Button Long Press
#define USER_WAIT 3000 //Next page Timeout
#if not defined(__AVR_ATmega328P__)
#error Sorry, this program works only on Arduino Uno
#endif
#if defined(LCD_PRINT) && defined(DEBUG_PRINT)
#error Invalid Parameters: Use LCD_PRINT or DEBUG_PRINT
#endif
#if defined(DEBUG_PRINT) && defined(ATSW)
#error Invalid Parameters: Use DEBUG_PRINT or ATSW
#endif
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <EEPROM.h>
#ifdef LCD_PRINT
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); //RS,E,D4,D5,D6,D7
#endif
#define UINT32_MAX ((uint32_t)-1)
#define ADC_PORT PORTC //ADC port data register
#define ADC_DDR DDRC //ADC port data direction register
#define ADC_PIN PINC //Port input pins register
#define TP1 0 //Test pin 1 (=0)
#define TP2 1 //Test pin 2 (=1)
#define TP3 2 //Test pin 3 (=2)
#define R_PORT PORTB //Port data register
#define R_DDR DDRB //Port data direction register
#define TEST_BUTTON A3 //Test/start push button (low active)
#define CYCLE_DELAY 3000
#define CYCLE_MAX 5
#define UREF_VCC 5001
#define UREF_OFFSET 1250
#define R_LOW 680
#define R_HIGH 470000
#define RH_OFFSET 700
#define R_ZERO 20
#define CAP_WIRES 15
#define CAP_PROBELEADS 9
#define CAP_DISCHARGED 2
#define ADC_SAMPLES 25
#define R_MCU_LOW 209 //Default: 209
#define R_MCU_HIGH 235 //Default: 235
#define COMPARATOR_OFFSET 15
#define CAP_PCB 42
#define C_ZERO CAP_PCB + CAP_WIRES + CAP_PROBELEADS
#define ADC_CLOCK_DIV (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)
#define CPU_FREQ F_CPU
#define OSC_STARTUP 16384
#define COMP_NONE 0
#define COMP_ERROR 1
#define COMP_MENU 2
#define COMP_RESISTOR 10
#define COMP_CAPACITOR 11
#define COMP_INDUCTOR 12
#define COMP_DIODE 20
#define COMP_BJT 21
#define COMP_FET 22
#define COMP_IGBT 23
#define COMP_TRIAC 24
#define COMP_THYRISTOR 25
#define LCD_CHAR_UNSET 0 //Just a place holder
#define LCD_CHAR_DIODE1 1 //Diode icon '>|'
#define LCD_CHAR_DIODE2 2 //Diode icon '|<'
#define LCD_CHAR_CAP 3 //Capacitor icon '||'
#define LCD_CHAR_FLAG 4 //Flag Icon
#define LCD_CHAR_RESIS1 6 //Resistor left icon '['
#define LCD_CHAR_RESIS2 7 //Resistor right icon ']'
#ifdef DEBUG_PRINT
#define LCD_CHAR_OMEGA 79
#define LCD_CHAR_MICRO '\u00B5' //Code for Arduino Serial Monitor
#else
#define LCD_CHAR_OMEGA 244 //Default: 244
#define LCD_CHAR_MICRO 228
#endif
#define TYPE_DISCHARGE 1 //Discharge error
#define TYPE_N_CHANNEL 0b00000001 //n channel
#define TYPE_P_CHANNEL 0b00000010 //p channel
#define TYPE_ENHANCEMENT 0b00000100 //Enhancement mode
#define TYPE_DEPLETION 0b00001000 //Depletion mode
#define TYPE_MOSFET 0b00010000 //MOSFET
#define TYPE_JFET 0b00100000 //JFET
#define TYPE_IGBT 0b01000000 //IGBT (no FET)
#define MODE_LOW_CURRENT 0b00000001 //Low test current
#define MODE_HIGH_CURRENT 0b00000010 //High test current
#define MODE_DELAYED_START 0b00000100 //Delayed start
#define TYPE_NPN 1 //NPN
#define TYPE_PNP 2 //PNP
#define MODE_CONTINOUS 0 //Continous
#define MODE_AUTOHOLD 1 //Auto hold
#define TABLE_SMALL_CAP 1
#define TABLE_LARGE_CAP 2
#define TABLE_INDUCTOR 3
#define FLAG_PULLDOWN 0b00000000
#define FLAG_PULLUP 0b00000001
#define FLAG_1MS 0b00001000
#define FLAG_10MS 0b00010000
typedef struct
{
byte TesterMode; //Tester operation mode
byte SleepMode; //MCU sleep mode
byte Samples; //Number of ADC samples
byte AutoScale; //Flag to disable/enable ADC auto scaling
byte RefFlag; //Internal control flag for ADC
unsigned int U_Bandgap; //Voltage of internal bandgap reference (mV)
unsigned int RiL; //Internal pin resistance of A‚ÂlC in low mode (0.1 Ohms)
unsigned int RiH; //Internal pin resistance of A‚ÂlC in high mode (0.1 Ohms)
unsigned int RZero; //Resistance of probe leads (2 in series) (0.01 Ohms)
byte CapZero; //Capacity zero offset (input + leads) (pF)
signed char RefOffset; //Voltage offset of bandgap reference (mV)
signed char CompOffset; //Voltage offset of analog comparator (mV)
} Config_Type;
typedef struct
{
byte Pin_1; //Probe-1
byte Pin_2; //Probe-2
byte Pin_3; //Probe-3
byte Rl_1; //Rl mask for probe-1
byte Rh_1; //Rh mask for probe-1
byte Rl_2; //Rl mask for probe-2
byte Rh_2; //Rh mask for probe-2
byte Rl_3; //Rl mask for probe-3
byte Rh_3; //Rh mask for probe-3
byte ADC_1; //ADC mask for probe-1
byte ADC_2; //ADC mask for probe-2
} Probe_Type;
typedef struct
{
byte Done; //Flag for transistor detection done
byte Found; //Component type which was found
byte Type; //Component specific subtype
byte Resistors; //Number of resistors found
byte Diodes; //Number of diodes found
byte Probe; //Error: probe pin
unsigned int U; //Error: voltage left in mV
} Check_Type;
typedef struct
{
byte A; //Probe pin #1
byte B; //Probe pin #2
byte Scale; //Exponent of factor (value * 10^x)
unsigned long Value; //Resistance
} Resistor_Type;
typedef struct
{
byte A; //Probe pin #1
byte B; //Probe pin #2
signed char Scale; //Exponent of factor (value * 10^x)
unsigned long Value; //Capacitance incl. zero offset
unsigned long Raw; //Capacitance excl. zero offset
} Capacitor_Type;
typedef struct
{
signed char Scale; //Exponent of factor (value * 10^x)
unsigned long Value; //Inductance
} Inductor_Type;
typedef struct
{
byte A; //Probe pin connected to anode
byte C; //Probe pin connected to cathode
unsigned int V_f; //Forward voltage in mV (high current)
unsigned int V_f2; //Forward voltage in mV (low current)
} Diode_Type;
typedef struct
{
byte B; //Probe pin connected to base
byte C; //Probe pin connected to collector
byte E; //Probe pin connected to emitter
unsigned long hFE; //Current amplification factor
unsigned int I_CE0; //Leakage current (in A‚ÂlA)
} BJT_Type;
typedef struct
{
byte G; //Test pin connected to gate
byte D; //Test pin connected to drain
byte S; //Test pin connected to source
unsigned int V_th; //Threshold voltage of gate in mV
} FET_Type;
typedef struct
{
} Error_Type;
char OutBuffer[12];
char PRGBuffer[32];
Config_Type Config; //Tester modes, offsets and values
//Probing
Probe_Type Probes; //Test probes
Check_Type Check; //Checking/testing
Resistor_Type Resistors[3]; //Resistors (3 combinations)
Capacitor_Type Caps[3]; //Capacitors (3 combinations)
Diode_Type Diodes[6]; //Diodes (3 combinations in 2 directions)
BJT_Type BJT; //Bipolar junction transistor
FET_Type FET; //FET
Inductor_Type Inductor; //Inductor
class __FlashStringHelper;
#define X(str) (strcpy_P(PRGBuffer, PSTR(str)), PRGBuffer)
const unsigned char Mode_str[] PROGMEM = "Mode:";
const unsigned char Continous_str[] PROGMEM = "Continous";
const unsigned char AutoHold_str[] PROGMEM = "Auto Hold";
const unsigned char Running_str[] PROGMEM = "Testing...";
const unsigned char Weak_str[] PROGMEM = "weak";
const unsigned char Low_str[] PROGMEM = "low";
const unsigned char Failed1_str[] PROGMEM = " No Component";
const unsigned char Failed2_str[] PROGMEM = " Found!";
const unsigned char Thyristor_str[] PROGMEM = "SCR";
const unsigned char Triac_str[] PROGMEM = "Triac";
const unsigned char GAK_str[] PROGMEM = "GAC=";
const unsigned char Done_str[] PROGMEM = " OK";
const unsigned char Select_str[] PROGMEM = "Select";
const unsigned char Selftest_str[] PROGMEM = "Selftest";
const unsigned char Adjustment_str[] PROGMEM = "Adjustment";
const unsigned char Default_str[] PROGMEM = "Default Values";
const unsigned char Save_str[] PROGMEM = "Save";
const unsigned char Show_str[] PROGMEM = "Show Values";
const unsigned char Remove_str[] PROGMEM = "Remove";
const unsigned char Create_str[] PROGMEM = "Create";
const unsigned char ShortCircuit_str[] PROGMEM = "Short Circuit!";
const unsigned char DischargeFailed_str[] PROGMEM = "Battery?";
const unsigned char Error_str[] PROGMEM = "Error!";
const unsigned char Battery_str[] PROGMEM = "Bat.";
const unsigned char OK_str[] PROGMEM = "ok";
const unsigned char MOS_str[] PROGMEM = "MOS";
const unsigned char FET_str[] PROGMEM = "FET";
const unsigned char Channel_str[] PROGMEM = "-ch";
const unsigned char Enhancement_str[] PROGMEM = "enh.";
const unsigned char Depletion_str[] PROGMEM = "dep.";
const unsigned char IGBT_str[] PROGMEM = "IGBT";
const unsigned char GateCap_str[] PROGMEM = "Cgs=";
const unsigned char GDS_str[] PROGMEM = "GDS=";
const unsigned char GCE_str[] PROGMEM = "GCE=";
const unsigned char NPN_str[] PROGMEM = "NPN";
const unsigned char PNP_str[] PROGMEM = "PNP";
const unsigned char EBC_str[] PROGMEM = "EBC=";
const unsigned char hFE_str[] PROGMEM = "h_FE=";
const unsigned char V_BE_str[] PROGMEM = "V_BE=";
const unsigned char I_CEO_str[] PROGMEM = "I_CEO=";
const unsigned char Vf_str[] PROGMEM = "Vf=";
const unsigned char DiodeCap_str[] PROGMEM = "C=";
const unsigned char Vth_str[] PROGMEM = "Vth=";
const unsigned char I_R_str[] PROGMEM = "I_R=";
const unsigned char URef_str[] PROGMEM = "Vref";
const unsigned char RhLow_str[] PROGMEM = "Rh-";
const unsigned char RhHigh_str[] PROGMEM = "Rh+";
const unsigned char RiLow_str[] PROGMEM = "Ri-";
const unsigned char RiHigh_str[] PROGMEM = "Ri+";
const unsigned char Rl_str[] PROGMEM = "+Rl-";
const unsigned char Rh_str[] PROGMEM = "+Rh-";
const unsigned char ProbeComb_str[] PROGMEM = "12 13 23";
const unsigned char CapOffset_str[] PROGMEM = "C0";
const unsigned char ROffset_str[] PROGMEM = "R0";
const unsigned char CompOffset_str[] PROGMEM = "AComp";
const unsigned char PWM_str[] PROGMEM = "PWM";
const unsigned char Hertz_str[] PROGMEM = "Hz";
const unsigned char Title_str[] PROGMEM = "Component Tester";
const unsigned char Organigation_str[] PROGMEM = " v1.0";
#ifdef DEBUG_PRINT
const unsigned char Cap_str[] PROGMEM = {'-', '|', '|', '-', 0};
const unsigned char Diode_AC_str[] PROGMEM = {'-', '>', '-', 0};
const unsigned char Diode_CA_str[] PROGMEM = {'-', '<', '-', 0};
const unsigned char Diodes_str[] PROGMEM = {'*', '>', ' ', ' ', 0};
const unsigned char Resistor_str[] PROGMEM = {'-', '[', ']', '-', 0};
#else
const unsigned char Cap_str[] PROGMEM = {'-', LCD_CHAR_CAP, '-', 0};
const unsigned char Diode_AC_str[] PROGMEM = {'-', LCD_CHAR_DIODE1, '-', 0};
const unsigned char Diode_CA_str[] PROGMEM = {'-', LCD_CHAR_DIODE2, '-', 0};
const unsigned char Diodes_str[] PROGMEM = {'*', LCD_CHAR_DIODE1, ' ', ' ', 0};
const unsigned char Resistor_str[] PROGMEM = {'-', LCD_CHAR_RESIS1, LCD_CHAR_RESIS2, '-', 0};
#endif
byte DiodeIcon1[8] = {0x11, 0x19, 0x1d, 0x1f, 0x1d, 0x19, 0x11, 0x00};
byte DiodeIcon2[8] = {0x11, 0x13, 0x17, 0x1f, 0x17, 0x13, 0x11, 0x00};
byte CapIcon[8] = {0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00};
byte ResIcon1[8] = {0x00, 0x0f, 0x08, 0x18, 0x08, 0x0f, 0x00, 0x00};
byte ResIcon2[8] = {0x00, 0x1e, 0x02, 0x03, 0x02, 0x1e, 0x00, 0x00};
byte FlagIcon[8] = {0x1f, 0x11, 0x0e, 0x04, 0x0a, 0x15, 0x1f, 0x00};
const unsigned char Prefix_table[] = {'p', 'n', LCD_CHAR_MICRO, 'm', 0, 'k', 'M'};
const unsigned int PWM_Freq_table[] = {100, 250, 500, 1000, 2500, 5000, 10000, 25000};
const unsigned int LargeCap_table[] = {23022, 21195, 19629, 18272, 17084, 16036, 15104, 14271, 13520, 12841, 12224, 11660, 11143, 10668, 10229, 9822, 9445, 9093, 8765, 8458, 8170, 7900, 7645, 7405, 7178, 6963, 6760, 6567, 6384, 6209, 6043, 5885, 5733, 5589, 5450, 5318, 5191, 5069, 4952, 4839, 4731, 4627, 4526, 4430, 4336};
const unsigned int SmallCap_table[] = {954, 903, 856, 814, 775, 740, 707, 676, 648};
const unsigned int Inductor_table[] = {4481, 3923, 3476, 3110, 2804, 2544, 2321, 2128, 1958, 1807, 1673, 1552, 1443, 1343, 1252, 1169, 1091, 1020, 953, 890, 831, 775, 721, 670, 621, 574, 527, 481, 434, 386, 334, 271};
const unsigned char Rl_table[] = {(1 << (TP1 * 2)), (1 << (TP2 * 2)), (1 << (TP3 * 2))};
const unsigned char ADC_table[] = {(1 << TP1), (1 << TP2), (1 << TP3)};
byte SmallCap(Capacitor_Type *Cap);
byte LargeCap(Capacitor_Type *Cap);
byte MeasureInductor(Resistor_Type *Resistor);
void ShowDiode_Uf(Diode_Type *Diode);
void ShowDiode_C(Diode_Type *Diode);
byte RunsPassed; //Counter for successful measurements
byte RunsMissed; //Counter for failed/missed measurements
byte ErrFnd; //An Error is occured
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
void setup()
{
byte Test; //Test value
power_spi_disable();
power_twi_disable();
power_timer2_disable();
#ifdef LCD_PRINT
lcd.begin(16, 2);
delay(5);
lcd.createChar(LCD_CHAR_DIODE1, DiodeIcon1); //Diode symbol |<|
lcd.createChar(LCD_CHAR_DIODE2, DiodeIcon2); //Diode symbol |<|
lcd.createChar(LCD_CHAR_CAP, CapIcon); //Capacitor symbol ||
lcd.createChar(LCD_CHAR_RESIS1, ResIcon1); //Resistor symbol [
lcd.createChar(LCD_CHAR_RESIS2, ResIcon2); //Resistor symbol ]
lcd.createChar(LCD_CHAR_FLAG, FlagIcon); //Flag symbol
lcd.home();
lcd_fixed_string(Title_str);
lcd_line(2);
lcd_fixed_string(Organigation_str);
#endif
#ifdef ATSW //Client Begin
Serial.begin(19200);
#endif
#ifdef DEBUG_PRINT
Serial.begin(9600); //Serial Output
#endif
ADCSRA = (1 << ADEN) | ADC_CLOCK_DIV; //Enable ADC and set clock divider
MCUSR &= ~(1 << WDRF); //Reset watchdog flag
DIDR0 = 0b00110111;
wdt_disable(); //Disable watchdog
Config.Samples = ADC_SAMPLES; //Number of ADC samples
Config.AutoScale = 1; //Enable ADC auto scaling
Config.RefFlag = 1; //No ADC reference set yet
delay(100);
RunsMissed = 0;
RunsPassed = 0;
Config.TesterMode = MODE_CONTINOUS; //Set default mode: continous
#ifdef BUTTON_INST
pinMode(TEST_BUTTON, INPUT_PULLUP); //Initialize the pushbutton pin as an input
#endif
LoadAdjust(); //Load adjustment values
#ifdef DEBUG_PRINT
Serial.print(X("A R D U T E S T E R "));
lcd_fixed_string(Organization_str); //Print Ardutester Version
Serial.println();
Serial.println(X(" By PighiXXX & PaoloP"));
Serial.println(X("original version by Markus Reschke"));
Serial.println();
#ifdef BUTTON_INST
Serial.print(X("Press Button to Probe"));
Serial.println(X(", long press enter Menu"));
#endif
#endif
delay(100);
}
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
void loop()
{
byte Test;
#ifdef BUTTON_INST
Test = TestKey(0, 0); //Wait user
#else
delay(3000); //No button installed, Wait 3 seconds
Test = 1; //No button, no menu :-)
#endif
#ifdef WDT_enabled
wdt_enable(WDTO_2S); //Enable watchdog (timeout 2s)
#endif
Check.Found = COMP_NONE;
Check.Type = 0;
Check.Done = 0;
Check.Diodes = 0;
Check.Resistors = 0;
BJT.hFE = 0;
BJT.I_CE0 = 0;
SetADCHiz(); //Set all pins of ADC port as input
lcd_clear(); //Clear LCD
#ifdef LCD_PRINT
lcd_fixed_string(Title_str);
lcd_line(2);
lcd_fixed_string(Organigation_str);
#endif
Config.U_Bandgap = ReadU(0x0e); //Dummy read for bandgap stabilization
Config.Samples = 200; //Do a lot of samples for high accuracy
Config.U_Bandgap = ReadU(0x0e); //Get voltage of bandgap reference
Config.Samples = ADC_SAMPLES; //Set samples back to default
Config.U_Bandgap += Config.RefOffset; //Add voltage offset
if (Test == 2) //Long Press
{
wdt_disable(); //Disable watchdog
MainMenu(); //Main Menu
}
else
{
if (AllProbesShorted() == 3) //All probes Shorted!
{
#ifdef DEBUG_PRINT
Serial.println();
#endif
lcd_fixed_string(Remove_str); //Display: Remove/Create
lcd_line(2);
lcd_fixed_string(ShortCircuit_str); //Display: short circuit!
}
else
{
lcd_line(2); //Move to line #2
lcd_fixed_string(Running_str); //Display: Testing...
DischargeProbes();
if (Check.Found == COMP_ERROR) //Discharge failed
{ //Only for Standalone Version!
lcd_fixed_string(DischargeFailed_str); //Display: Battery?
lcd_line(2);
lcd_testpin(Check.Probe);
lcd_data(':');
lcd_space();
DisplayValue(Check.U, -3, 'V');
}
else //Skip all other checks
{
CheckProbes(TP1, TP2, TP3);
CheckProbes(TP2, TP1, TP3);
CheckProbes(TP1, TP3, TP2);
CheckProbes(TP3, TP1, TP2);
CheckProbes(TP2, TP3, TP1);
CheckProbes(TP3, TP2, TP1);
if ((Check.Found == COMP_NONE) ||
(Check.Found == COMP_RESISTOR))
{
#ifdef DEBUG_PRINT
Serial.println();
Serial.println(X("Wait a moment..."));
#else
lcd_clear_line(2);
lcd_fixed_string(Running_str);
lcd_data('.');
#endif
MeasureCap(TP3, TP1, 0);
#ifdef LCD_PRINT
lcd_data('.');
#endif
MeasureCap(TP3, TP2, 1);
#ifdef LCD_PRINT
lcd_data('.');
#endif
MeasureCap(TP2, TP1, 2);
}
lcd_clear();
#ifdef BUTTON_INST
pinMode(TEST_BUTTON, INPUT_PULLUP); //Reinitialize the pushbutton pin as an input
#endif
#ifdef DEBUG_PRINT
Serial.print("Found: ");
switch (Check.Found)
{
case COMP_ERROR:
Serial.println(X("Component Error!"));
break;
case COMP_NONE:
Serial.println(X("No Component!"));
break;
case COMP_RESISTOR:
Serial.println(X("Resistor"));
break;
case COMP_CAPACITOR:
Serial.println(X("Capacitor"));
break;
case COMP_INDUCTOR:
Serial.println(X("Inductor"));
break;
case COMP_DIODE:
Serial.println(X("Diode"));
break;
case COMP_BJT:
Serial.println(X("BJT"));
break;
case COMP_FET:
Serial.println(X("FET"));
break;
case COMP_IGBT:
Serial.println(X("IGBT"));
break;
case COMP_TRIAC:
Serial.println(X("TRIAC"));
break;
case COMP_THYRISTOR:
Serial.println(X("Thyristor"));
break;
}
#endif
switch (Check.Found)
{
case COMP_ERROR:
ShowError();
break;
case COMP_DIODE:
ShowDiode();
break;
case COMP_BJT:
ShowBJT();
break;
case COMP_FET:
ShowFET();
break;
case COMP_IGBT:
ShowIGBT();
break;
case COMP_THYRISTOR:
ShowSpecial();
break;
case COMP_TRIAC:
ShowSpecial();
break;
case COMP_RESISTOR:
ShowResistor();
break;
case COMP_CAPACITOR:
ShowCapacitor();
break;
default: //No component found
ShowFail();
}
#ifdef ATSW //Client output
Serial.println("@>");
Serial.println(Check.Found);
Serial.println("|");
Serial.println(Check.Type);
Serial.println("|");
Serial.println(Check.Done);
Serial.println("|");
Serial.println("@<");
#endif
RunsMissed = 0; //Reset counter
RunsPassed++; //Increase counter
}
}
}
delay(1000); //Let the user read the text
wdt_disable(); //Disable watchdog
}//end of void loop
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
void SetADCHiz(void)
{
ADC_DDR &= ~(1 << TP1);
ADC_DDR &= ~(1 << TP2);
ADC_DDR &= ~(1 << TP3);
}
void SetADCLow(void)
{
ADC_PORT &= ~(1 << TP1);
ADC_PORT &= ~(1 << TP2);
ADC_PORT &= ~(1 << TP3);
}
void UpdateProbes(byte Probe1, byte Probe2, byte Probe3)
{
Probes.Pin_1 = Probe1;
Probes.Pin_2 = Probe2;
Probes.Pin_3 = Probe3;
Probes.Rl_1 = Rl_table[Probe1];
Probes.Rh_1 = Probes.Rl_1 + Probes.Rl_1;
Probes.ADC_1 = ADC_table[Probe1];
Probes.Rl_2 = Rl_table[Probe2];
Probes.Rh_2 = Probes.Rl_2 + Probes.Rl_2;
Probes.ADC_2 = ADC_table[Probe2];
Probes.Rl_3 = Rl_table[Probe3];
Probes.Rh_3 = Probes.Rl_3 + Probes.Rl_3;
}
byte ShortedProbes(byte Probe1, byte Probe2)
{
byte Flag = 0; //Return value
unsigned int U1; //Voltage at probe #1 in mV
unsigned int U2; //Voltage at probe #2 in mV
R_PORT = Rl_table[Probe1];
R_DDR = Rl_table[Probe1] | Rl_table[Probe2];
U1 = ReadU(Probe1);
U2 = ReadU(Probe2);
if ((U1 > UREF_VCC / 2 - 30) && (U1 < UREF_VCC / 2 + 30))
{
if ((U2 > UREF_VCC / 2 - 30) && (U2 < UREF_VCC / 2 + 30))
{
Flag = 1;
}
}
R_DDR = 0;
return Flag;
}
byte AllProbesShorted(void)
{
byte Flag = 0; //Return value
Flag = ShortedProbes(TP1, TP2);
Flag += ShortedProbes(TP1, TP3);
Flag += ShortedProbes(TP2, TP3);
return Flag;
}
void DischargeProbes(void)
{
byte Counter; //Loop control
byte Limit = 40; //Sliding timeout (2s)
byte ID; //Test pin
byte DischargeMask; //Bitmask
unsigned int U_c; //Current voltage
unsigned int U_old[3]; //Old voltages
SetADCHiz();
SetADCLow();
R_PORT = 0;
R_DDR = (2 << (TP1 * 2)) | (2 << (TP2 * 2)) | (2 << (TP3 * 2));
R_DDR |= (1 << (TP1 * 2)) | (1 << (TP2 * 2)) | (1 << (TP3 * 2));
U_old[0] = ReadU(TP1);
U_old[1] = ReadU(TP2);
U_old[2] = ReadU(TP3);
Counter = 1;
ID = 2;
DischargeMask = 0;
while (Counter > 0)
{
ID++; //Next probe
if (ID > 2) ID = 0; //Start with probe #1 again
if (DischargeMask & (1 << ID)) //Skip discharged probe
continue;
U_c = ReadU(ID); //Get voltage of probe
if (U_c < U_old[ID]) //Voltage decreased
{
U_old[ID] = U_c; //Update old value
if ((Limit - Counter) < 20)
{
if (Limit < (255 - 20)) Limit += 20;
}
Counter = 1; //Reset no-changes counter
}
else //Voltage not decreased
{
if ((U_c < 10) && (Limit <= 40)) Limit = 80;
Counter++; //Increase no-changes counter
}
if (U_c <= CAP_DISCHARGED) //Seems to be discharged
{
DischargeMask |= (1 << ID); //Set flag
}
else if (U_c < 800) //Extra pull-down
{
ADC_DDR |= ADC_table[ID];
}
if (DischargeMask == 0b00000111) //All probes discharged
{
Counter = 0; //End loop
}
else if (Counter > Limit) //No decrease for some time
{
//Might be a battery or a super cap
Check.Found = COMP_ERROR; //Report error
Check.Type = TYPE_DISCHARGE; //Discharge problem
Check.Probe = ID; //Save probe
Check.U = U_c; //Save voltage
Counter = 0; //End loop
}
else //Go for another round
{
wdt_reset(); //Reset watchdog
delay(50); //Wait for 50ms
}
}
R_DDR = 0; //Set resistor port to input mode
SetADCHiz(); //Set ADC port to input mode
}
void PullProbe(byte Mask, byte Mode)
{
if (Mode & FLAG_PULLUP) R_PORT |= Mask; //Pull-up
else R_PORT &= ~Mask; //Pull-down
R_DDR |= Mask; //Enable pulling
if (Mode & FLAG_1MS) delay(1); //Wait 1ms
else delay(10); //Wait 10ms
R_DDR &= ~Mask; //Set to HiZ mode
R_PORT &= ~Mask; //Set 0
}
unsigned long RescaleValue(unsigned long Value, signed char Scale, signed char NewScale)
{
unsigned long NewValue;
NewValue = Value; //Take old value
while (Scale != NewScale) //Processing loop
{
if (NewScale > Scale) //Upscale
{
NewValue /= 10;
Scale++;
}
else //Downscale
{
NewValue *= 10;
Scale--;
}
}
return NewValue;
}
unsigned int GetFactor(unsigned int U_in, byte ID)
{
unsigned int Factor; //Return value
unsigned int U_Diff; //Voltage difference to table start
unsigned int Fact1, Fact2; //Table entries
unsigned int TabStart; //Table start voltage
unsigned int TabStep; //Table step voltage
unsigned int TabIndex; //Table entries (-2)
unsigned int *Table;
byte Index; //Table index
byte Diff; //Difference to next entry
if (ID == TABLE_SMALL_CAP)
{
TabStart = 1000; //Table starts at 1000mV
TabStep = 50; //50mV steps between entries
TabIndex = 7; //Entries in table - 2
Table = (unsigned int *)&SmallCap_table[0]; //Pointer to table start
}
else if (ID == TABLE_LARGE_CAP)
{
TabStart = 300; //Table starts at 1000mV
TabStep = 25; //25mV steps between entries
TabIndex = 42; //Entries in table - 2
Table = (unsigned int *)&LargeCap_table[0]; //Pointer to table start
}
else if (ID == TABLE_INDUCTOR)
{
TabStart = 200; //Table starts at 200
TabStep = 25; //Steps between entries
TabIndex = 30; //Entries in table - 2
Table = (unsigned int *)&Inductor_table[0]; //Pointer to table start
}
else
{
return 0;
}
if (U_in >= TabStart) U_Diff = U_in - TabStart;
else U_Diff = 0;
Index = U_Diff / TabStep; //Index (position in table)
Diff = U_Diff % TabStep; //Difference to index
Diff = TabStep - Diff; //Difference to next entry
if (Index > TabIndex) Index = TabIndex;
Table += Index; //Advance to index
Fact1 = *(Table);
Table++; //Next entry
Fact2 = *(Table);
Factor = Fact1 - Fact2;
Factor *= Diff;
Factor += TabStep / 2;
Factor /= TabStep;
Factor += Fact2;
return Factor;
}
void CheckProbes(byte Probe1, byte Probe2, byte Probe3)
{
byte Flag; //Temporary value
unsigned int U_Rl; //Voltage across Rl (load)
unsigned int U_1; //Voltage #1
if (Check.Found == COMP_ERROR) return; //Skip check on any error
wdt_reset(); //Reset watchdog
UpdateProbes(Probe1, Probe2, Probe3); //Update bitmasks
R_PORT = 0; //Set resistor port to Gnd
R_DDR = Probes.Rl_2; //Pull down probe-2 via Rl
ADC_DDR = Probes.ADC_1; //Set probe-1 to output
ADC_PORT = Probes.ADC_1; //Pull-up probe-1 directly
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLDOWN);
U_Rl = ReadU_5ms(Probes.Pin_2); //Get voltage at Rl
if (U_Rl >= 977) // > 1.4mA
{
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLUP);
U_Rl = ReadU_5ms(Probes.Pin_2); //Get voltage at Rl
}
if (U_Rl > 490) // > 700A‚ÂlA (was 92mV/130A‚ÂlA)
{
CheckDepletionModeFET(U_Rl);
}
if (U_Rl < 977) //Load current < 1.4mA
{
if (Check.Done == 0) //Not sure yet
{
R_DDR = Probes.Rl_2; //Enable Rl for probe-2
R_PORT = 0; //Pull down collector via Rl
ADC_DDR = Probes.ADC_1; //Set probe 1 to output
ADC_PORT = Probes.ADC_1; //Pull up emitter directly
delay(5);
R_DDR = Probes.Rl_2 | Probes.Rl_3; //Pull down base via Rl
U_1 = ReadU_5ms(Probe2); //Get voltage at collector
if (U_1 > 3422) //Detected current > 4.8mA
{
CheckBJTorEnhModeMOSFET(TYPE_PNP, U_Rl);
}
}
if (Check.Done == 0) //Not sure yet
{
ADC_DDR = Probes.ADC_2; //Set probe-2 to output mode
SetADCLow(); //Pull down probe-2 directly
R_DDR = Probes.Rl_1 | Probes.Rl_3; //Select Rl for probe-1 & Rl for probe-3
R_PORT = Probes.Rl_1 | Probes.Rl_3; //Pull up collector & base via Rl
U_1 = ReadU_5ms(Probe1); //Get voltage at collector
if (U_1 < 1600) //Detected current > 4.8mA
{
Flag = CheckThyristorTriac();
if (Flag == 0) //No thyristor or triac
{
CheckBJTorEnhModeMOSFET(TYPE_NPN, U_Rl);
}
}
}
}
else //Load current > 1.4mA
{
CheckDiode();
}
if ((Check.Found == COMP_NONE) ||
(Check.Found == COMP_RESISTOR))
{
CheckResistor();
}
else
{
if ((Check.Found == COMP_FET) && (Check.Type & TYPE_MOSFET))
VerifyMOSFET();
}
SetADCHiz(); //Set ADC port to HiZ mode
SetADCLow(); //Set ADC port low
R_DDR = 0; //Set resistor port to HiZ mode
R_PORT = 0; //Set resistor port low
}
unsigned int ReadU(byte Probe)
{
unsigned int U; //Return value (mV)
byte Counter; //Loop counter
unsigned long Value; //ADC value
boolean cycle;
Probe |= (1 << REFS0); //Use internal reference anyway
do {
cycle = false;
ADMUX = Probe; //Set input channel and U reference
Counter = Probe & (1 << REFS1); //Get REFS1 bit flag
if (Counter != Config.RefFlag)
{
waitus(100); //Time for voltage stabilization
ADCSRA |= (1 << ADSC); //Start conversion
while (ADCSRA & (1 << ADSC)); //Wait until conversion is done
Config.RefFlag = Counter; //Update flag
}
Value = 0UL; //Reset sampling variable
Counter = 0; //Reset counter
while (Counter < Config.Samples) //Take samples
{
ADCSRA |= (1 << ADSC); //Start conversion
while (ADCSRA & (1 << ADSC)); //Wait until conversion is done
Value += ADCW; //Add ADC reading
if (Counter == 4)
{
if (((unsigned int)Value < 1024) && !(Probe & (1 << REFS1)) && (Config.AutoScale == 1))
{
Probe |= (1 << REFS1); //Select internal bandgap reference
cycle = true; //Re-run sampling
break;
}
}
Counter++; //One less to do
}
} while (cycle);
if (Probe & (1 << REFS1)) U = Config.U_Bandgap;//Bandgap reference
else U = UREF_VCC; //Vcc reference
Value *= U; //ADC readings * U_ref
Value /= 1024; // / 1024 for 10bit ADC
Value /= Config.Samples;
U = (unsigned int)Value;
return U;
}
unsigned int ReadU_5ms(byte Probe)
{
delay(5); //Wait 5ms
return (ReadU(Probe));
}
unsigned int ReadU_20ms(byte Probe)
{
delay(20); //Wait 20ms
return (ReadU(Probe));
}
void waitus(byte microsec) {
delayMicroseconds(microsec);
}
unsigned long Get_hFE_C(byte Type)
{
unsigned long hFE; //Return value
unsigned int U_R_e; //Voltage across emitter resistor
unsigned int U_R_b; //Voltage across base resistor
unsigned int Ri; //Internal resistance of A‚ÂlC
if (Type == TYPE_NPN) //NPN
{
ADC_DDR = Probes.ADC_1; //Set probe 1 to output
ADC_PORT = Probes.ADC_1; //Pull up collector directly
R_DDR = Probes.Rl_2 | Probes.Rl_3; //Select Rl for probe-2 & Rl for probe-3
R_PORT = Probes.Rl_3; //Pull up base via Rl
U_R_e = ReadU_5ms(Probes.Pin_2); //U_R_e = U_e
U_R_b = UREF_VCC - ReadU(Probes.Pin_3); //U_R_b = Vcc - U_b
}
else //PNP
{
SetADCLow(); //Set ADC port low
ADC_DDR = Probes.ADC_2; //Pull down collector directly
R_PORT = Probes.Rl_1; //Pull up emitter via Rl
R_DDR = Probes.Rl_1 | Probes.Rl_3; //Pull down base via Rl
U_R_e = UREF_VCC - ReadU_5ms(Probes.Pin_1); //U_R_e = Vcc - U_e
U_R_b = ReadU(Probes.Pin_3); //U_R_b = U_b
}
if (U_R_b < 10) //I_b < 14A‚ÂlA -> Darlington
{
if (Type == TYPE_NPN) //NPN
{
R_DDR = Probes.Rl_2 | Probes.Rh_3; //Select Rl for probe-2 & Rh for probe-3
R_PORT = Probes.Rh_3; //Pull up base via Rh
U_R_e = ReadU_5ms(Probes.Pin_2); //U_R_e = U_e
U_R_b = UREF_VCC - ReadU(Probes.Pin_3); //U_R_b = Vcc - U_b
Ri = Config.RiL; //Get internal resistor
}
else //PNP
{
R_DDR = Probes.Rl_1 | Probes.Rh_3; //Pull down base via Rh
U_R_e = UREF_VCC - ReadU_5ms(Probes.Pin_1);//U_R_e = Vcc - U_e
U_R_b = ReadU(Probes.Pin_3); //U_R_b = U_b
Ri = Config.RiH; //Get internal resistor
}
if (U_R_b < 1) U_R_b = 1; //Prevent division by zero
hFE = U_R_e * R_HIGH; //U_R_e * R_b
hFE /= U_R_b; // / U_R_b
hFE *= 10; //Upscale to 0.1
hFE /= (R_LOW * 10) + Ri; // / R_e in 0.1 Ohm
}
else //I_b > 14A‚ÂlA -> standard
{
hFE = (unsigned long)((U_R_e - U_R_b) / U_R_b);
}
return hFE;
}
void GetGateThreshold(byte Type)
{
unsigned long Uth = 0; //Gate threshold voltage
byte Drain_Rl; //Rl bitmask for drain
byte Drain_ADC; //ADC bitmask for drain
byte PullMode;
byte Counter; //Loop counter
if (Type & TYPE_N_CHANNEL) //n-channel
{
Drain_Rl = Probes.Rl_1;
Drain_ADC = Probes.ADC_1;
PullMode = FLAG_10MS | FLAG_PULLDOWN;
}
else //p-channel
{
Drain_Rl = Probes.Rl_2;
Drain_ADC = Probes.ADC_2;
PullMode = FLAG_10MS | FLAG_PULLUP;
}
Drain_ADC &= 0b00000111; //drain
ADMUX = Probes.Pin_3 | (1 << REFS0); //Select probe-3 for ADC input
for (Counter = 0; Counter < 10; Counter++)
{
wdt_reset(); //Reset watchdog
PullProbe(Probes.Rl_3, PullMode);
R_DDR = Drain_Rl | Probes.Rh_3;
if (Type & TYPE_N_CHANNEL) //n-channel
{
while (ADC_PIN & Drain_ADC);
}
else //p-channel
{
while (!(ADC_PIN & Drain_ADC));
}
R_DDR = Drain_Rl; //Set probe-3 to HiZ mode
ADCSRA |= (1 << ADSC); //Start ADC conversion
while (ADCSRA & (1 << ADSC)); //Wait until conversion is done
if (Type & TYPE_N_CHANNEL) //n-channel
{
Uth += ADCW; //U_g = U_measued
}
else //p-channel
{
Uth += (1023 - ADCW); //U_g = Vcc - U_measured
}
}
Uth /= 10; //Average of 10 samples
Uth *= UREF_VCC; //Convert to voltage
Uth /= 1024; //Using 10 bit resolution
FET.V_th = (unsigned int)Uth;
}
unsigned int GetLeakageCurrent(void)
{
unsigned int I_leak = 0; //Return value
unsigned int U_Rl; //Voltage at Rl
unsigned int R_Shunt; //Shunt resistor
uint32_t Value;
R_PORT = 0; //Set resistor port to Gnd
R_DDR = Probes.Rl_2; //Pull down probe-2 via Rl
ADC_DDR = Probes.ADC_1; //Set probe-1 to output
ADC_PORT = Probes.ADC_1; //Pull-up probe-1 directly
U_Rl = ReadU_5ms(Probes.Pin_2); //Get voltage at Rl
R_Shunt = Config.RiL + (R_LOW * 10); //Consider internal resistance of MCU (0.1 Ohms)
R_Shunt += 5; //For rounding
R_Shunt /= 10; //Scale to Ohms
Value = U_Rl * 100000; //Scale to 10nV
Value /= R_Shunt; //in 10nA
Value += 55; //For rounding
Value /= 100; //Scale to A‚ÂlA
I_leak = Value;
SetADCHiz(); //Set ADC port to HiZ mode
SetADCLow(); //Set ADC port low
R_DDR = 0; //Set resistor port to HiZ mode
R_PORT = 0; //Set resistor port low
return I_leak;
}
void CheckDiode(void)
{
Diode_Type *Diode; //Pointer to diode
unsigned int U1_Rl; //Vf #1 with Rl pull-up
unsigned int U1_Rh; //Vf #1 with Rh pull-up
unsigned int U1_Zero; //Vf #1 zero
unsigned int U2_Rl; //Vf #2 with Rl pull-up
unsigned int U2_Rh; //Vf #2 with Rh pull-up
unsigned int U2_Zero; //Vf #2 zero
wdt_reset(); //Reset watchdog
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return; //Skip on error
SetADCLow();
ADC_DDR = Probes.ADC_2; //Pull down cathode directly
U1_Zero = ReadU(Probes.Pin_1); //Get voltage at anode
R_DDR = Probes.Rh_1; //Enable Rh for probe-1
R_PORT = Probes.Rh_1; //Pull up anode via Rh
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLUP);
U1_Rh = ReadU_5ms(Probes.Pin_1); //Get voltage at anode, neglect voltage at cathode
R_DDR = Probes.Rl_1; //Enable Rl for probe-1
R_PORT = Probes.Rl_1; //Pull up anode via Rl
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLUP);
U1_Rl = ReadU_5ms(Probes.Pin_1); //Get voltage at anode
U1_Rl -= ReadU(Probes.Pin_2); //Substract voltage at cathode
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return; //Skip on error
SetADCLow();
ADC_DDR = Probes.ADC_2; //Pull down cathode directly
U2_Zero = ReadU(Probes.Pin_1); //Get voltage at anode
R_DDR = Probes.Rh_1; //Enable Rh for probe-1
R_PORT = Probes.Rh_1; //Pull up anode via Rh
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLDOWN);
U2_Rh = ReadU_5ms(Probes.Pin_1); //Get voltage at anode, neglect voltage at cathode
R_DDR = Probes.Rl_1; //Enable Rl for probe-1
R_PORT = Probes.Rl_1; //Pull up anode via Rl
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLDOWN);
U2_Rl = ReadU_5ms(Probes.Pin_1); //Get voltage at anode
U2_Rl -= ReadU(Probes.Pin_2); //Substract voltage at cathode
R_PORT = 0; //Stop pulling up
if (U1_Rl > U2_Rl) //The higher voltage wins
{
U2_Rl = U1_Rl;
U2_Rh = U1_Rh;
U2_Zero = U1_Zero;
}
if (U2_Rh <= 10) return; //Small resistor or very large cap
U1_Zero = U2_Rh - U2_Zero; //Voltage difference
if ((U2_Zero > 2) && (U1_Zero < 100)) return; //Capacitor
if (U2_Rh < 40) //Resistor (< 3k)
{
uint32_t a, b;
b = (R_HIGH * 10) / ((R_LOW * 10) + Config.RiH + Config.RiL);
a = b - 1; //k - 1
a /= 5; // / 5V
a *= U2_Rh; // *U_Rh
a += 1000; // +1 (1000 for mV)
b *= 1000; //For mV
b *= U2_Rh; // *U_Rh
b /= a; //U_Rl in mV
U1_Zero = (unsigned int)b;
U1_Rl = U1_Zero;
U1_Rh = U1_Zero;
U1_Zero /= 50; //2%
U1_Rh += U1_Zero; //102%
U1_Zero = (unsigned int)b;
U1_Zero /= 33; //3%
U1_Rl -= U1_Zero; //97% (for resistors near 1k)
if ((U2_Rl >= U1_Rl) && (U2_Rl <= U1_Rh)) return;
}
if ((U2_Rl > 150) && (U2_Rl < 4640))
{
if ((Check.Found == COMP_NONE) ||
(Check.Found == COMP_RESISTOR))
{
Check.Found = COMP_DIODE;
}
Diode = &Diodes[Check.Diodes];
Diode->A = Probes.Pin_1;
Diode->C = Probes.Pin_2;
Diode->V_f = U2_Rl; //Vf for high measurement current
Diode->V_f2 = U2_Rh; //Vf for low measurement current
Check.Diodes++;
}
}
void VerifyMOSFET(void)
{
byte Flag = 0;
byte n = 0;
byte Anode;
byte Cathode;
Diode_Type *Diode; //Pointer to diode
if (Check.Type & TYPE_N_CHANNEL) //n-channel
{
Anode = FET.S;
Cathode = FET.D;
}
else //p-channel
{
Anode = FET.D;
Cathode = FET.S;
}
Diode = &Diodes[0]; //First diode
while (n < Check.Diodes)
{
if ((Diode->A == Cathode) && (Diode->C == Anode))
{
Flag = 1; //Signal match
n = 10; //End loop
}
n++; //Next diode
Diode++;
}
if (Flag == 1) //Found reversed diode
{
Check.Found = COMP_NONE;
Check.Type = 0;
Check.Done = 0;
}
}
void CheckBJTorEnhModeMOSFET(byte BJT_Type, unsigned int U_Rl)
{
byte FET_Type; //MOSFET type
unsigned int U_R_c; //Voltage across collector resistor
unsigned int U_R_b; //Voltage across base resistor
unsigned int BJT_Level; //Voltage threshold for BJT
unsigned int FET_Level; //Voltage threshold for FET
unsigned int I_CE0; //Leakage current
unsigned long hFE_C; //hFE (common collector)
unsigned long hFE_E; //hFE (common emitter)
if (BJT_Type == TYPE_NPN) //NPN / n-channel
{
BJT_Level = 2557; //Voltage across base resistor (5.44A‚ÂlA)
FET_Level = 3400; //Voltage across drain resistor (4.8mA)
FET_Type = TYPE_N_CHANNEL;
R_DDR = Probes.Rl_1 | Probes.Rh_3; //Enable Rl for probe-1 & Rh for probe-3
R_PORT = Probes.Rl_1 | Probes.Rh_3; //Pull up collector via Rl and base via Rh
delay(50); //Wait to skip gate charging of a FET
U_R_c = UREF_VCC - ReadU(Probes.Pin_1); //U_R_c = Vcc - U_c
U_R_b = UREF_VCC - ReadU(Probes.Pin_3); //U_R_b = Vcc - U_b
}
else //PNP / p-channel
{
BJT_Level = 977; //Voltage across base resistor (2.1A‚ÂlA)
FET_Level = 2000; //Voltage across drain resistor (2.8mA)
FET_Type = TYPE_P_CHANNEL;
R_DDR = Probes.Rl_2 | Probes.Rh_3; //Pull down base via Rh
U_R_c = ReadU_5ms(Probes.Pin_2); //U_R_c = U_c
U_R_b = ReadU(Probes.Pin_3); //U_R_b = U_b
}
if (U_R_b > BJT_Level) //U_R_b exceeds minimum level of BJT
{
if (Check.Found == COMP_BJT) Check.Done = 1;
Check.Found = COMP_BJT;
Check.Type = BJT_Type;
I_CE0 = GetLeakageCurrent(); //Get leakage current (in A‚ÂlA)
if (U_R_c > U_Rl) U_R_c -= U_Rl; // - U_Rl (leakage)
hFE_E = U_R_c * R_HIGH; //U_R_c * R_b
hFE_E /= U_R_b; // / U_R_b
hFE_E *= 10; //Upscale to 0.1
if (BJT_Type == TYPE_NPN) //NPN
hFE_E /= (R_LOW * 10) + Config.RiH; // / R_c in 0.1 Ohm
else //PNP
hFE_E /= (R_LOW * 10) + Config.RiL; // / R_c in 0.1 Ohm
hFE_C = Get_hFE_C(BJT_Type);
if (hFE_C > hFE_E) hFE_E = hFE_C;
if (hFE_E > BJT.hFE)
{
BJT.hFE = hFE_E;
BJT.I_CE0 = I_CE0;
BJT.B = Probes.Pin_3;
if (BJT_Type == TYPE_NPN) //NPN
{
BJT.C = Probes.Pin_1;
BJT.E = Probes.Pin_2;
}
else //PNP
{
BJT.C = Probes.Pin_2;
BJT.E = Probes.Pin_1;
}
}
#if 0
SetADCHiz(); //Set ADC port to HiZ mode
R_DDR = 0; //Set resistor port to HiZ mode
if (BJT_Type == TYPE_NPN) //NPN
{
SetADCLow();
ADC_DDR = Probes.ADC_1; //Pull-down emitter directly
R_PORT = Probes.Rl_2 | Probes.Rh_3; //Pull-up base via Rh
R_DDR = Probes.Rl_2 | Probes.Rh_3; //Enable probe resistors
U_R_b = UREF_VCC - ReadU_5ms(Probes.Pin_2);//U_R_c = Vcc - U_c
}
else //PNP
{
R_PORT = 0;
R_DDR = Probes.Rl_1 | Probes.Rh_3; //Pull down base via Rh
ADC_DDR = Probes.ADC_2;
ADC_PORT = Probes.ADC_2; //Pull-up emitter directly
U_R_b = ReadU_5ms(Probes.Pin_1); //U_R_c = U_c
}
if (U_R_c > U_R_b) //I_c > I_c_reversed
{
Check.Done = 1;
}
#endif
}
else if ((U_Rl < 97) && (U_R_c > FET_Level)) //No BJT
{
I_CE0 = ReadU(Probes.Pin_1) - ReadU(Probes.Pin_2);
if (I_CE0 < 250) //MOSFET
{
Check.Found = COMP_FET;
Check.Type = FET_Type | TYPE_ENHANCEMENT | TYPE_MOSFET;
}
else //IGBT
{
Check.Found = COMP_IGBT;
Check.Type = FET_Type | TYPE_ENHANCEMENT;
}
Check.Done = 1; //Transistor found
GetGateThreshold(FET_Type);
FET.G = Probes.Pin_3;
if (FET_Type == TYPE_N_CHANNEL) //n-channel
{
FET.D = Probes.Pin_1;
FET.S = Probes.Pin_2;
}
else //p-channel
{
FET.D = Probes.Pin_2;
FET.S = Probes.Pin_1;
}
}
}
void CheckDepletionModeFET(unsigned int U_Rl_L)
{
unsigned int U_1; //Voltage #1
unsigned int U_2; //Voltage #2
if (Check.Done == 0) //No transistor found yet
{
R_DDR = Probes.Rl_2 | Probes.Rh_3; //Pull down gate via Rh
U_1 = ReadU_20ms(Probes.Pin_2); //Voltage at source
R_PORT = Probes.Rh_3; //Pull up gate via Rh
U_2 = ReadU_20ms(Probes.Pin_2); //Voltage at source
if (U_2 > (U_1 + 488))
{
SetADCLow(); //Set ADC port to low
ADC_DDR = Probes.ADC_2; //Pull down source directly
R_DDR = Probes.Rl_1 | Probes.Rh_3; //Enable Rl for probe-1 & Rh for probe-3
R_PORT = Probes.Rl_1 | Probes.Rh_3; //Pull up drain via Rl / pull up gate via Rh
U_2 = ReadU_20ms(Probes.Pin_3); //Get voltage at gate
if (U_2 > 3911) //MOSFET
{
Check.Type = TYPE_N_CHANNEL | TYPE_DEPLETION | TYPE_MOSFET;
}
else //JFET
{
Check.Type = TYPE_N_CHANNEL | TYPE_JFET;
}
Check.Found = COMP_FET;
Check.Done = 1;
FET.G = Probes.Pin_3;
FET.D = Probes.Pin_1;
FET.S = Probes.Pin_2;
}
}
if (Check.Done == 0) //No transistor found yet
{
SetADCLow(); //Set ADC port to Gnd
ADC_DDR = Probes.ADC_2; //Pull down drain directly
R_DDR = Probes.Rl_1 | Probes.Rh_3; //Enable Rl for probe-1 & Rh for probe-3
R_PORT = Probes.Rl_1 | Probes.Rh_3; //Pull up source via Rl / pull up gate via Rh
U_1 = ReadU_20ms(Probes.Pin_1); //Get voltage at source
R_PORT = Probes.Rl_1; //Pull down gate via Rh
U_2 = ReadU_20ms(Probes.Pin_1); //Get voltage at source
if (U_1 > (U_2 + 488))
{
ADC_PORT = Probes.ADC_1; //Pull up source directly
ADC_DDR = Probes.ADC_1; //Enable pull up for source
U_2 = ReadU_20ms(Probes.Pin_3); //Get voltage at gate
if (U_2 < 977) //MOSFET
{
Check.Type = TYPE_P_CHANNEL | TYPE_DEPLETION | TYPE_MOSFET;
}
else //JFET
{
Check.Type = TYPE_P_CHANNEL | TYPE_DEPLETION | TYPE_JFET;
}
Check.Found = COMP_FET;
Check.Done = 1;
FET.G = Probes.Pin_3;
FET.D = Probes.Pin_2;
FET.S = Probes.Pin_1;
}
}
}
byte CheckThyristorTriac(void)
{
byte Flag = 0; //Return value
unsigned int U_1; //Voltage #1
unsigned int U_2; //Voltage #2
PullProbe(Probes.Rl_3, FLAG_10MS | FLAG_PULLDOWN);
U_1 = ReadU_5ms(Probes.Pin_1); //Get voltage at anode
R_PORT = 0; //Pull down anode
delay(5);
R_PORT = Probes.Rl_1; //And pull up anode again
U_2 = ReadU_5ms(Probes.Pin_1); //Get voltage at anode (below Rl)
if ((U_1 < 1600) && (U_2 > 4400))
{
Check.Found = COMP_THYRISTOR; //If not detected as a triac below
Check.Done = 1;
R_DDR = 0; //Disable all probe resistors
R_PORT = 0;
ADC_PORT = Probes.ADC_2; //Pull up MT1 directly
delay(5);
R_DDR = Probes.Rl_1; //Pull down MT2 via Rl
U_1 = ReadU_5ms(Probes.Pin_1); //Get voltage at MT2
if (U_1 <= 244)
{
R_DDR = Probes.Rl_1 | Probes.Rl_3; //And pull down gate via Rl
U_1 = ReadU_5ms(Probes.Pin_3); //Get voltage at gate
U_2 = ReadU(Probes.Pin_1); //Get voltage at MT2
if ((U_1 >= 977) && (U_2 >= 733))
{
R_DDR = Probes.Rl_1; //Set probe3 to HiZ mode
U_1 = ReadU_5ms(Probes.Pin_1); //Get voltage at MT2
if (U_1 >= 733)
{
R_PORT = Probes.Rl_1; //Pull up MT2 via Rl
delay(5);
R_PORT = 0; //And pull down MT2 via Rl
U_1 = ReadU_5ms(Probes.Pin_1); //Get voltage at MT2
if (U_1 <= 244)
{
Check.Found = COMP_TRIAC;
}
}
}
}
BJT.B = Probes.Pin_3;
BJT.C = Probes.Pin_1;
BJT.E = Probes.Pin_2;
Flag = 1; //Signal that we found a component
}
return Flag;
}
unsigned int SmallResistor(byte ZeroFlag)
{
unsigned int R = 0; //Return value
byte Probe; //Probe ID
byte Mode; //Measurement mode
byte Counter; //Sample counter
unsigned long Value; //ADC sample value
unsigned long Value1 = 0; //U_Rl temp. value
unsigned long Value2 = 0; //U_R_i_L temp. value
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return R; //Skip on error
SetADCLow(); //Set ADC port to low
ADC_DDR = Probes.ADC_2; //Pull-down probe 2 directly
R_PORT = 0; //Low by default
R_DDR = Probes.Rl_1; //Enable resistor
#define MODE_HIGH 0b00000001
#define MODE_LOW 0b00000010
Mode = MODE_HIGH;
while (Mode > 0)
{
if (Mode & MODE_HIGH) Probe = Probes.Pin_1;
else Probe = Probes.Pin_2;
wdt_reset(); //Reset watchdog
Counter = 0; //Reset loop counter
Value = 0; //Reset sample value
Probe |= (1 << REFS0) | (1 << REFS1);
ADMUX = Probe; //Set input channel and U reference
waitus(100); //Time for voltage stabilization
ADCSRA |= (1 << ADSC); //Start conversion
while (ADCSRA & (1 << ADSC)); //Wait until conversion is done
while (Counter < 100)
{
ADC_DDR = Probes.ADC_2; //Pull-down probe-2 directly
R_PORT = Probes.Rl_1;
ADCSRA |= (1 << ADSC); //Start conversion
waitus(20);
R_PORT = 0;
ADC_DDR = Probes.ADC_2 | Probes.ADC_1;
while (ADCSRA & (1 << ADSC)); //Wait until conversion is done
Value += ADCW; //Add ADC reading
waitus(900);
Counter++; //Next round
}
Value *= Config.U_Bandgap;
Value /= 1024; // / 1024 for 10bit ADC
Value /= 10; //De-sample to 0.1mV
if (Mode & MODE_HIGH) //Probe #1 / Rl
{
Mode = MODE_LOW; //Switch to low side
Value1 = Value; //Save measured value
}
else //Probe #2 / R_i_L
{
Mode = 0; //End loop
Value2 = Value; //Save measured value
}
}
if (Value1 > Value2) //Sanity check
{
Value = 10UL * UREF_VCC; //in 0.1 mV
Value -= Value1;
Value *= 1000; //Scale to A‚ÂlA
Value /= ((R_LOW * 10) + Config.RiH); //in 0.1 Ohms
Value1 -= Value2; //in 0.1 mV
Value1 *= 10000; //Scale to 0.01 A‚ÂlV
Value1 /= Value; //in 0.01 Ohms
R = (unsigned int)Value1; //Copy result
if (ZeroFlag == 1) //Auto-zero
{
if (R > Config.RZero) R -= Config.RZero;
else R = 0;
}
}
#undef MODE_LOW
#undef MODE_HIGH
Config.RefFlag = (1 << REFS1); //Set REFS1 bit flag
return R;
}
void CheckResistor(void)
{
Resistor_Type *Resistor; //Pointer to resistor
unsigned long Value1; //Resistance of measurement #1
unsigned long Value2; //Resistance of measurement #2
unsigned long Value; //Resistance value
unsigned long Temp; //Temp. value
signed char Scale; //Resistance scale
signed char Scale2; //Resistance scale
byte n; //Counter
unsigned int U_Rl_H; //Voltage #1
unsigned int U_Ri_L; //Voltage #2
unsigned int U_Rl_L; //Voltage #3
unsigned int U_Ri_H; //Voltage #4
unsigned int U_Rh_H; //Voltage #5
unsigned int U_Rh_L; //Voltage #6
wdt_reset(); //Reset watchdog
SetADCLow(); //Set ADC port low low
ADC_DDR = Probes.ADC_2; //Pull down probe-2 directly
R_DDR = Probes.Rl_1; //Enable Rl for probe-1
R_PORT = Probes.Rl_1; //Pull up probe-1 via Rl
U_Ri_L = ReadU_5ms(Probes.Pin_2); //Get voltage at internal R of A‚ÂlC
U_Rl_H = ReadU(Probes.Pin_1); //Get voltage at Rl pulled up
R_PORT = 0; //Set resistor port low
R_DDR = Probes.Rh_1; //Pull down probe-1 via Rh
U_Rh_L = ReadU_5ms(Probes.Pin_1); //Get voltage at probe 1
if (U_Rh_L <= 20)
{
R_PORT = Probes.Rh_1; //Pull up probe-1 via Rh
U_Rh_H = ReadU_5ms(Probes.Pin_1); //Get voltage at Rh pulled up
ADC_DDR = Probes.ADC_1; //Set probe-1 to output
ADC_PORT = Probes.ADC_1; //Pull up probe-1 directly
R_PORT = 0; //Set resistor port to low
R_DDR = Probes.Rl_2; //Pull down probe-2 via Rl
U_Ri_H = ReadU_5ms(Probes.Pin_1); //Get voltage at internal R of A‚ÂlC
U_Rl_L = ReadU(Probes.Pin_2); //Get voltage at Rl pulled down
R_DDR = Probes.Rh_2; //Pull down probe-2 via Rh
U_Rh_L = ReadU_5ms(Probes.Pin_2); //Get voltage at Rh pulled down
if ((U_Rl_H >= 4400) || (U_Rh_H <= 97)) //R >= 5.1k / R < 9.3k
{
if (U_Rh_H < 4972) //R < 83.4M & prevent division by zero
{
Value = 0; //Reset value of resistor
if (U_Rl_L < 169) //R > 19.5k
{
if (U_Rh_L >= 38) //R < 61.4M & prevent division by zero
{
Value1 = R_HIGH * U_Rh_H;
Value1 /= (UREF_VCC - U_Rh_H);
Value2 = R_HIGH * (UREF_VCC - U_Rh_L);
Value2 /= U_Rh_L;
if (U_Rh_H < 990) //Below bandgap reference
{
Value = (Value1 * 4);
Value += Value2;
Value /= 5;
}
else if (U_Rh_L < 990) //Below bandgap reference
{
Value = (Value2 * 4);
Value += Value1;
Value /= 5;
}
else //Higher than bandgap reference
{
Value = (Value1 + Value2) / 2;
}
Value += RH_OFFSET; //Add offset value for Rh
Value *= 10; //Upscale to 0.1 Ohms
}
}
else //U_Rl_L: R <= 19.5k
{
if ((U_Rl_H >= U_Ri_L) && (U_Ri_H >= U_Rl_L))
{
if (U_Rl_H == UREF_VCC) U_Rl_H = UREF_VCC - 1;
Value1 = (R_LOW * 10) + Config.RiH; //Rl + RiH in 0.1 Ohm
Value1 *= (U_Rl_H - U_Ri_L);
Value1 /= (UREF_VCC - U_Rl_H);
Value2 = (R_LOW * 10) + Config.RiL; //Rl + RiL in 0.1 Ohms
Value2 *= (U_Ri_H - U_Rl_L);
Value2 /= U_Rl_L;
if (U_Rl_H < 990) //Below bandgap reference
{
Value = (Value1 * 4);
Value += Value2;
Value /= 5;
}
else if (U_Rl_L < 990) //Below bandgap reference
{
Value = (Value2 * 4);
Value += Value1;
Value /= 5;
}
else //Higher than bandgap reference
{
Value = (Value1 + Value2) / 2;
}
}
else //May happen for very low resistances
{
if (U_Rl_L > 4750) Value = 1; //U_Rl_L: R < 15 Ohms
}
}
if (Value > 0) //Valid resistor
{
Scale = -1; //0.1 Ohm by default
if (Value < 100UL)
{
Value2 = (unsigned long)SmallResistor(1);
Scale2 = -2; //0.01 Ohm
Value1 = Value * 2; //Allow 100% tolerance
Value1 *= 10; //Re-scale to 0.01 Ohms
if (Value1 > Value2) //Got expected value
{
Value = Value2; //Update data
Scale = Scale2;
}
}
n = 0;
while (n < Check.Resistors) //Loop through resistors
{
Resistor = &Resistors[n]; //Pointer to element
if ((Resistor->A == Probes.Pin_1) && (Resistor->B == Probes.Pin_2))
{
if (CmpValue(Value, Scale, 2, 0) == -1)
{
Temp = Value / 2; //50%
}
else //>= 2 Ohm
{
Temp = Value / 20; //5%
}
Value1 = Value - Temp; //95% or 50%
Value2 = Value + Temp; //105% or 150%
if (CmpValue(Value, Scale, 1, -1) == -1)
{
Value1 = 0; //0
Value2 = Value * 5; //500%
if (Value2 == 0) Value2 = 5; //Special case
}
if ((CmpValue(Resistor->Value, Resistor->Scale, Value1, Scale) >= 0) &&
(CmpValue(Resistor->Value, Resistor->Scale, Value2, Scale) <= 0))
{
Check.Found = COMP_RESISTOR;
n = 100; //End loop and signal match
}
else //No match
{
n = 200; //End loop and signal mis-match
}
}
else //No match
{
n++; //Next one
}
}
if (n != 100) //Not a known resistor
{
if (Check.Resistors < 3) //Prevent array overflow
{
Resistor = &Resistors[Check.Resistors];
Resistor->A = Probes.Pin_2;
Resistor->B = Probes.Pin_1;
Resistor->Value = Value;
Resistor->Scale = Scale;
Check.Resistors++; //Another one found
}
}
}
}
}
}
}
signed char CmpValue(unsigned long Value1, signed char Scale1, unsigned long Value2, signed char Scale2)
{
signed char Flag; //Return value
signed char Len1, Len2; //Length
Len1 = NumberOfDigits(Value1) + Scale1;
Len2 = NumberOfDigits(Value2) + Scale2;
if ((Value1 == 0) || (Value2 == 0)) //Special case
{
Flag = 10; //Perform direct comparison
}
else if (Len1 > Len2) //More digits -> larger
{
Flag = 1;
}
else if (Len1 == Len2) //Same length
{
Len1 -= Scale1;
Len2 -= Scale2;
while (Len1 > Len2) //Up-scale Value #2
{
Value2 *= 10;
Len2++;
}
while (Len2 > Len1) //Up-scale Value #1
{
Value1 *= 10;
Len1++;
}
Flag = 10; //Perform direct comparison
}
else //Less digits -> smaller
{
Flag = -1;
}
if (Flag == 10) //Perform direct comparison
{
if (Value1 > Value2) Flag = 1;
else if (Value1 < Value2) Flag = -1;
else Flag = 0;
}
return Flag;
}
byte NumberOfDigits(unsigned long Value)
{
byte Counter = 1;
while (Value >= 10)
{
Value /= 10;
Counter++;
}
return Counter;
}
byte LargeCap(Capacitor_Type *Cap)
{
byte Flag = 3; //Return value
byte TempByte; //Temp. value
byte Mode; //Measurement mode
signed char Scale; //Capacitance scale
unsigned int TempInt; //Temp. value
unsigned int Pulses; //Number of charging pulses
unsigned int U_Zero; //Voltage before charging
unsigned int U_Cap; //Voltage of DUT
unsigned int U_Drop = 0; //Voltage drop
unsigned long Raw; //Raw capacitance value
unsigned long Value; //Corrected capacitance value
boolean rerun;
Mode = FLAG_10MS | FLAG_PULLUP; //Start with large caps
do {
rerun = false; //One-Time
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return 0; //Skip on error
SetADCLow(); //Set ADC port to low
ADC_DDR = Probes.ADC_2; //Pull-down probe 2 directly
R_PORT = 0; //Set resistor port to low
R_DDR = 0; //Set resistor port to HiZ
U_Zero = ReadU(Probes.Pin_1); //Get zero voltage (noise)
Pulses = 0;
TempByte = 1;
while (TempByte)
{
Pulses++;
PullProbe(Probes.Rl_1, Mode); //Charging pulse
U_Cap = ReadU(Probes.Pin_1); //Get voltage
U_Cap -= U_Zero; //Zero offset
if ((Pulses == 126) && (U_Cap < 75)) TempByte = 0;
if (U_Cap >= 300) TempByte = 0;
if (Pulses == 500) TempByte = 0;
wdt_reset(); //Reset watchdog
}
if (U_Cap < 300)
{
Flag = 1;
}
if ((Pulses == 1) && (U_Cap > 1300))
{
if (Mode & FLAG_10MS) //<47A‚ÂlF
{
Mode = FLAG_1MS | FLAG_PULLUP; //Set mode (1ms charging pulses)
rerun = true; //And re-run
}
else //<4.7A‚ÂlF
{
Flag = 2;
}
}
} while (rerun);
if (Flag == 3)
{
TempInt = Pulses;
while (TempInt > 0)
{
TempInt--; //Descrease timeout
U_Drop = ReadU(Probes.Pin_1); //Get voltage
U_Drop -= U_Zero; //Zero offset
wdt_reset(); //Reset watchdog
}
if (U_Cap > U_Drop) U_Drop = U_Cap - U_Drop;
else U_Drop = 0;
if (U_Drop > 100) Flag = 0;
}
if (Flag == 3)
{
Scale = -9; //Factor is scaled to nF
Raw = GetFactor(U_Cap + U_Drop, TABLE_LARGE_CAP);
Raw *= Pulses; //C = pulses * factor
if (Mode & FLAG_10MS) Raw *= 10; // *10 for 10ms charging pulses
if (Raw > UINT32_MAX / 1000) //Scale down if C >4.3mF
{
Raw /= 1000; //Scale down by 10^3
Scale += 3; //Add 3 to the exponent
}
Value = Raw; //Copy raw value
Value *= 100;
if (Mode & FLAG_10MS) Value /= 109; //-9% for large cap
else Value /= 104; //-4% for mid cap
Cap->A = Probes.Pin_2; //Pull-down probe pin
Cap->B = Probes.Pin_1; //Pull-up probe pin
Cap->Scale = Scale; //-9 or -6
Cap->Raw = Raw;
Cap->Value = Value; //Max. 4.3*10^6nF or 100*10^3A‚ÂlF
}
return Flag;
}
byte SmallCap(Capacitor_Type *Cap)
{
byte Flag = 3; //Return value
byte TempByte; //Temp. value
signed char Scale; //Capacitance scale
unsigned int Ticks; //Timer counter
unsigned int Ticks2; //Timer overflow counter
unsigned int U_c; //Voltage of capacitor
unsigned long Raw; //Raw capacitance value
unsigned long Value; //Corrected capacitance value
Ticks2 = 0; //Reset timer overflow counter
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return 0; //Skip on error
R_PORT = 0; //Set resistor port to low
ADC_DDR = (1 << TP1) | (1 << TP2) | (1 << TP3);
SetADCLow(); //Set ADC port to low
R_DDR = Probes.Rh_1; //Pull-down probe-1 via Rh
ADCSRB = (1 << ACME); //Use ADC multiplexer as negative input
ACSR = (1 << ACBG) | (1 << ACIC); //Use bandgap as positive input, trigger timer1
ADMUX = (1 << REFS0) | Probes.Pin_1; //Switch ADC multiplexer to probe 1 and set AREF to Vcc
ADCSRA = ADC_CLOCK_DIV; //Disable ADC, but keep clock dividers
waitus(200);
TCCR1A = 0; //Set default mode
TCCR1B = 0; //Set more timer modes
TCNT1 = 0; //Set Counter1 to 0
TIFR1 = (1 << ICF1) | (1 << OCF1B) | (1 << OCF1A) | (1 << TOV1);
R_PORT = Probes.Rh_1; //Pull-up probe-1 via Rh
if (Check.Found == COMP_FET)
{
TempByte = (((1 << TP1) | (1 << TP2) | (1 << TP3)) & ~(1 << Probes.Pin_1));
}
else
{
TempByte = Probes.ADC_2; //Keep just probe-1 pulled down
}
TCCR1B = (1 << CS10);
ADC_DDR = TempByte; //Start charging DUT
while (1)
{
TempByte = TIFR1; //Get timer1 flags
if (TempByte & (1 << ICF1)) break;
if (TempByte & (1 << TOV1))
{
TIFR1 = (1 << TOV1); //Reset flag
wdt_reset(); //Reset watchdog
Ticks2++; //Increase overflow counter
if (Ticks2 == (CPU_FREQ / 5000)) break;
}
}
TCCR1B = 0; //Stop timer
TIFR1 = (1 << ICF1); //Reset Input Capture flag
Ticks = ICR1; //Get counter value
R_DDR = 0; //Set resistor port to HiZ mode
if ((TCNT1 > Ticks) && (TempByte & (1 << TOV1)))
{
TIFR1 = (1 << TOV1); //Reset overflow flag
Ticks2++; //Increase overflow counter
}
ADCSRA = (1 << ADEN) | (1 << ADIF) | ADC_CLOCK_DIV;
U_c = ReadU(Probes.Pin_1); //Get voltage of cap
R_PORT = 0; //Pull down probe-2 via Rh
R_DDR = Probes.Rh_1; //Enable Rh for probe-1 again
if (Ticks2 >= (CPU_FREQ / 5000)) Flag = 1;
if (Flag == 3)
{
Raw = (unsigned long)Ticks; //Set lower 16 bits
Raw |= (unsigned long)Ticks2 << 16; //Set upper 16 bits
if (Raw > 2) Raw -= 2; //Subtract processing time overhead
Scale = -12; //Default factor is for pF scale
if (Raw > (UINT32_MAX / 1000)) //Prevent overflow (4.3*10^6)
{
Raw /= 1000; //Scale down by 10^3
Scale += 3; //Add 3 to the exponent (nF)
}
Raw *= GetFactor(Config.U_Bandgap + Config.CompOffset, TABLE_SMALL_CAP);
Raw /= (CPU_FREQ / 10000);
Value = Raw; //Take raw value
if (Scale == -12) //pF scale
{
if (Value >= Config.CapZero) //If value is larger than offset
{
Value -= Config.CapZero; //Substract offset
}
else //If value is smaller than offset
{
Value = 0; //Set value to 0
}
}
Cap->A = Probes.Pin_2; //Pull-down probe pin
Cap->B = Probes.Pin_1; //Pull-up probe pin
Cap->Scale = Scale; //-12 or -9
Cap->Raw = Raw;
Cap->Value = Value; //Max. 5.1*10^6pF or 125*10^3nF
if (((Scale == -12) && (Value >= 100000)) ||
((Scale == -9) && (Value <= 20000)))
{
signed int Offset;
signed long TempLong;
while (ReadU(Probes.Pin_1) > 980)
{
}
R_DDR = 0; //Stop discharging
Config.AutoScale = 0; //Disable auto scaling
Ticks = ReadU(Probes.Pin_1); //U_c with Vcc reference
Config.AutoScale = 1; //Enable auto scaling again
Ticks2 = ReadU(Probes.Pin_1); //U_c with bandgap reference
R_DDR = Probes.Rh_1; //Resume discharging
Offset = Ticks - Ticks2;
if ((Offset < -4) || (Offset > 4)) //Offset too large
{
TempLong = Offset;
TempLong *= Config.U_Bandgap; // * U_ref
TempLong /= Ticks2; // / U_c
Config.RefOffset = (signed char)TempLong;
}
Offset = U_c - Config.U_Bandgap;
if ((Offset > -50) && (Offset < 50)) Config.CompOffset = Offset;
}
}
return Flag;
}
void MeasureCap(byte Probe1, byte Probe2, byte ID)
{
byte TempByte; //Temp. value
Capacitor_Type *Cap; //Pointer to cap data structure
Diode_Type *Diode; //Pointer to diode data structure
Resistor_Type *Resistor; //Pointer to resistor data structure
Cap = &Caps[ID];
Cap->A = 0;
Cap->B = 0;
Cap->Scale = -12; //pF by default
Cap->Raw = 0;
Cap->Value = 0;
if (Check.Found == COMP_ERROR) return; //Skip check on any error
if (Check.Found == COMP_RESISTOR)
{
Resistor = &Resistors[0]; //Pointer to first resistor
TempByte = 0;
while (TempByte < Check.Resistors)
{
if (((Resistor->A == Probe1) && (Resistor->B == Probe2)) ||
((Resistor->A == Probe2) && (Resistor->B == Probe1)))
{
if (CmpValue(Resistor->Value, Resistor->Scale, 10UL, 0) == -1)
TempByte = 99; //Signal low resistance and end loop
}
TempByte++; //Next one
Resistor++; //Next one
}
if (TempByte != 100) return; //Skip this one
}
Diode = &Diodes[0]; //Pointer to first diode
for (TempByte = 0; TempByte < Check.Diodes; TempByte++)
{
if ((Diode->C == Probe2) &&
(Diode->A == Probe1) &&
(Diode->V_f < 1500))
{
return;
}
Diode++; //Next one
}
UpdateProbes(Probe1, Probe2, 0); //Update bitmasks and probes
TempByte = LargeCap(Cap);
if (TempByte == 2)
{
TempByte = SmallCap(Cap);
}
if (Check.Diodes == 0)
{
if (Check.Found == COMP_RESISTOR)
{
if (Cap->Scale >= -6) Check.Found = COMP_CAPACITOR;
}
else if ((Cap->Scale > -12) || (Cap->Value >= 5UL))
{
Check.Found = COMP_CAPACITOR; //Report capacitor
}
}
DischargeProbes(); //Discharge DUT
SetADCHiz(); //Set ADC port to input
SetADCLow(); //Set ADC port low
R_DDR = 0; //Set resistor port to input
R_PORT = 0; //Set resistor port low
}
byte MeasureInductance(uint32_t *Time, byte Mode)
{
byte Flag = 3; //Return value
byte Test; //Test flag
signed char Offset; //Counter offet
unsigned int Ticks_L; //Timer counter
unsigned int Ticks_H; //Timer overflow counter
unsigned long Counter; //Counter
if (Time == NULL) return 0;
DischargeProbes(); //Try to discharge probes
if (Check.Found == COMP_ERROR) return 0;
R_PORT = 0; //Set resistor port to low
SetADCLow(); //Set ADC port to low
if (Mode & MODE_LOW_CURRENT) //Low current
{
R_DDR = Probes.Rl_2; //Pull down probe-2 via Rl
ADC_DDR = Probes.ADC_1; //Pull down probe-1 directly
}
else //High current
{
R_DDR = 0; //Disable probe resistors
ADC_DDR = Probes.ADC_1 | Probes.ADC_2;
}
ADCSRB = (1 << ACME); //Use ADC multiplexer as negative input
ACSR = (1 << ACBG) | (1 << ACIC); //Use bandgap as positive input, trigger timer1
ADMUX = (1 << REFS0) | Probes.Pin_2; //Switch ADC multiplexer to probe-2 and set AREF to Vcc
ADCSRA = ADC_CLOCK_DIV; //Disable ADC, but keep clock dividers
waitus(200); //Allow bandgap reference to settle
Ticks_H = 0; //Reset timer overflow counter
TCCR1A = 0; //Set default mode
TCCR1B = 0; //Set more timer modes
TCNT1 = 0; //Set Counter1 to 0
TIFR1 = (1 << ICF1) | (1 << OCF1B) | (1 << OCF1A) | (1 << TOV1);
if (Mode & MODE_DELAYED_START) //Delayed start
{
Test = (CPU_FREQ / 1000000); //Cycles per A‚Âls
ADC_PORT = Probes.ADC_1; //Pull up probe-1 directly
while (Test > 0)
{
Test--;
asm volatile("nop\n\t"::);
}
TCCR1B |= (1 << CS10); //Start timer (1/1 clock divider)
}
else //Immediate start
{
TCCR1B |= (1 << CS10); //Start timer (1/1 clock divider)
ADC_PORT = Probes.ADC_1; //Pull up probe-1 directly
}
while (1)
{
Test = TIFR1; //Get timer1 flags
if (Test & (1 << ICF1)) break;
if (Test & (1 << TOV1))
{
TIFR1 = (1 << TOV1); //Reset flag
wdt_reset(); //Reset watchdog
Ticks_H++; //Increase overflow counter
if (Ticks_H == (CPU_FREQ / 250000))
{
Flag = 0; //Signal timeout
break; //End loop
}
}
}
TCCR1B = 0; //Stop timer
TIFR1 = (1 << ICF1); //Reset Input Capture flag
Ticks_L = ICR1; //Get counter value
R_DDR = Probes.Rl_2 | Probes.Rl_1;
SetADCHiz();
if ((TCNT1 > Ticks_L) && (Test & (1 << TOV1)))
{
TIFR1 = (1 << TOV1); //Reset overflow flag
Ticks_H++; //Increase overflow counter
}
ADCSRA = (1 << ADEN) | (1 << ADIF) | ADC_CLOCK_DIV;
Counter = (unsigned long)Ticks_L; //Lower 16 bits
Counter |= (unsigned long)Ticks_H << 16; //Upper 16 bits
Offset = -4; //Subtract processing overhead
if (Mode & MODE_DELAYED_START) //Delayed start
{
}
else //Immediate start
{
Offset -= 1; //Timer started one cycle too early
}
if (Offset >= 0) //Positive offet
{
Counter += Offset;
}
else //Negative offset
{
Offset *= -1; //Make it positive
if (Counter < Offset) Counter = 0; //Prevent underflow
else Counter -= Offset; //Subtract offset
}
if (Counter > 0)
{
Counter += (CPU_FREQ / 2000000); //Add half of cycles for rounding
Counter /= (CPU_FREQ / 1000000); //Divide by frequeny and scale to A‚Âls
}
if (Counter <= 1) Flag = 2; //Signal inductance too low
*Time = Counter; //Save time
return Flag;
}
byte MeasureInductor(Resistor_Type *Resistor)
{
byte Test = 0; //Return value / measurement result
byte Mode; //Measurement mode
byte Scale; //Scale of value
unsigned int R_total; //Total resistance
unsigned int Factor; //Factor
unsigned long Value; //Value
unsigned long Time1; //Time #1
unsigned long Time2; //Time #2
Inductor.Scale = 0;
Inductor.Value = 0;
if (Resistor == NULL) return Test;
if (CmpValue(Resistor->Value, Resistor->Scale, 2000, 0) >= 0) return Test;
UpdateProbes(Resistor->A, Resistor->B, 0); //Update probes
Mode = MODE_LOW_CURRENT;
Test = MeasureInductance(&Time1, Mode);
if (Test == 2) //Inductance too low
{
if (CmpValue(Resistor->Value, Resistor->Scale, 40, 0) < 0)
{
Mode = MODE_HIGH_CURRENT;
Test = MeasureInductance(&Time1, Mode);
}
}
else if (Test == 3) //Valid time
{
Mode = MODE_LOW_CURRENT | MODE_DELAYED_START;
Test = MeasureInductance(&Time2, Mode);
if (Time1 > Time2) Time1 = Time2; //Lower value wins
}
if (Test != 3) Test = 0; //Measurements faile
if (Test == 3)
{
R_total = RescaleValue(Resistor->Value, Resistor->Scale, -1);
R_total += Config.RiH + Config.RiL;
Factor = Config.RiL;
if (Mode & MODE_LOW_CURRENT) //Low current measurement mode
{
R_total += (R_LOW * 10);
Factor += (R_LOW * 10);
}
Value = Config.U_Bandgap + Config.CompOffset;
Value *= R_total; // * R_total (in 0.1 Ohms)
Value /= Factor; // / R_shunt (in 0.1 Ohms)
Value /= 5; // / 5000mV, * 10^3
Scale = -6; //A‚ÂlH by default
Value = Time1; //t_stop
Value *= Factor; // * factor (A‚Âls * 10^-3)
while (Value > 100000) //Re-scale to prevent overflow
{
Value /= 10;
Scale++;
}
Value *= R_total; // * R_total (in 0.1 Ohms)
Value /= 10000;
Inductor.Scale = Scale;
Inductor.Value = Value;
Test = 1; //Signal success
}
return Test;
}
void lcd_clear(void)
{
#ifdef LCD_PRINT
lcd.clear();
delay(2); //LCD needs some time for processing
#endif
#ifdef DEBUG_PRINT
Serial.println();
#endif
}
void lcd_line(unsigned char Line)
{
#ifdef LCD_PRINT
lcd.setCursor(0, Line);
#endif
#ifdef DEBUG_PRINT
Serial.println();
#endif
}
void lcd_clear_line(unsigned char Line)
{
unsigned char Pos;
#ifdef LCD_PRINT
lcd_line(Line); //Go to beginning of line
for (Pos = 0; Pos < 20; Pos++) //For 20 times
{
lcd_data(' '); //Send space
}
lcd_line(Line); //Go back to beginning of line
#endif
#ifdef DEBUG_PRINT
Serial.println();
#endif
}
void lcd_testpin(unsigned char Probe)
{
lcd_data('1' + Probe); //Send data
}
void lcd_space(void)
{
lcd_data(' ');
}
void lcd_string(char *String)
{
while (*String) //Loop until trailing 0 is reached
{
lcd_data(*String); //Send character
String++; //Next one
}
}
void lcd_fixed_string(const unsigned char *String)
{
while (pgm_read_byte(String) != 0x00)
lcd_data(pgm_read_byte(String++)); //Send character
}
void lcd_data(unsigned char Data)
{
#ifdef LCD_PRINT
lcd.write(Data); //Send data to LCD
#endif
#ifdef DEBUG_PRINT
Serial.write(Data); //Send data to Serial
#endif
}
void DisplayValue(unsigned long Value, signed char Exponent, unsigned char Unit)
{
unsigned char Prefix = 0; //Prefix character
byte Offset = 0; //Exponent offset to next 10^3 step
byte Index; //Index ID
byte Length; //String length
while (Value >= 10000)
{
Value += 5; //For automagic rounding
Value = Value / 10; //Scale down by 10^1
Exponent++; //Increase exponent by 1
}
if (Exponent >= -12) //Prevent index underflow
{
Exponent += 12; //Shift exponent to be >= 0
Index = Exponent / 3; //Number of 10^3 steps
Offset = Exponent % 3; //Offset to lower 10^3 step
if (Offset > 0) //Dot required
{
Index++; //Upscale prefix
Offset = 3 - Offset; //Reverse value (1 or 2)
}
if (Index <= 6) Prefix = *(&Prefix_table[Index]);
}
utoa((unsigned int)Value, OutBuffer, 10);
Length = strlen(OutBuffer);
Exponent = Length - Offset; //Calculate position
if (Exponent <= 0) //We have to prepend "0."
{
lcd_data('0');
lcd_data('.');
if (Exponent < 0) lcd_data('0'); //Extra 0 for factor 100
}
if (Offset == 0) Exponent = -1; //Disable dot if not needed
Exponent--;
Index = 0;
while (Index < Length) //Loop through string
{
lcd_data(OutBuffer[Index]); //Display char
if (Index == Exponent) lcd_data('.'); //Display dot
Index++; //Next one
}
if (Prefix) lcd_data(Prefix);
if (Unit) lcd_data(Unit);
}
void DisplaySignedValue(signed long Value, signed char Exponent, unsigned char Unit)
{
if (Value < 0) //Negative value
{
lcd_data('-'); //Display: "-"
Value = -Value; //Make value positive
}
DisplayValue((signed long)Value, Exponent, Unit);
}
void ShortCircuit(byte Mode)
{
byte Run = 0; //Loop control
byte Test; //Test feedback
unsigned char *String = NULL; //Display string pointer
Test = AllProbesShorted(); //Get current status
if (Mode == 0) //Remove short
{
if (Test != 0) String = (unsigned char *)Remove_str;
}
else //Create short
{
if (Test != 3) String = (unsigned char *)Create_str;
}
if (String)
{
lcd_clear();
lcd_fixed_string(String); //Display: Remove/Create
lcd_line(2);
lcd_fixed_string(ShortCircuit_str); //Display: short circuit!
Run = 1; //Enter loop
}
while (Run == 1)
{
Test = AllProbesShorted(); //Check for short circuits
if (Mode == 0) //Remove short
{
if (Test == 0) Run = 0; //End loop if all removed
}
else //Create short
{
if (Test == 3) Run = 0; //End loop if all shorted
}
if (Run == 1) //If not done yet
delay(50); //Wait a little bit
else //If done
delay(200); //Time to debounce
}
}
byte TestKey(unsigned int Timeout, byte Mode)
{
byte Flag = 0; //Return value
byte Run = 1; //Loop control
byte Counter = 0; //Time counter
byte ButtonStatus = 0; //Button Status
if (Mode > 10) //Consider operation mode
{
if (Config.TesterMode == MODE_AUTOHOLD) //Auto hold mode
{
Timeout = 0; //Disable timeout
Mode -= 10; //Set cursor mode
}
else //Continous mode
{
Mode = 0; //Disable cursor
}
}
if (Mode > 0) //Cursor enabled
{
#ifdef LCD_PRINT
lcd.setCursor(15, 2);
lcd.cursor();
#endif
}
while (Run)
{
//Take care about timeout
if (Timeout > 0) //Timeout enabled
{
#ifdef LCD_PRINT
lcd.setCursor(15, 2);
lcd_data(LCD_CHAR_FLAG);
#endif
if (Timeout > 5) Timeout -= 5; //Decrease timeout by 5ms
else Run = 0; //End loop on timeout
}
if (!(digitalRead(TEST_BUTTON))) //If key is pressed
{
Counter = 0; //Reset counter
delay(30); //Time to debounce
while (Run) //Detect how long key is pressed
{
if (!(digitalRead(TEST_BUTTON))) //Key still pressed
{
Counter++; //Increase counter
if (Counter > LONG_PRESS) Run = 0; //End loop if LONG_PRESS are reached
else delay(10); //Otherweise wait 10ms
}
else //Key released
{
Run = 0; //End loop
}
}
if (Counter > LONG_PRESS) Flag = 2; //Long (>= LONG_PRESS)
else Flag = 1; //Short (< LONG_PRESS)
}
else //No key press
{
delay(5); //Wait a little bit more (5ms)
if (Mode == 2) //Blinking cursor
{
Counter++; //Increase counter
if (Counter == 100) //Every 500ms (2Hz)
{
Counter = 0; //Reset counter
if (Run == 1) //Turn off
{
#ifdef LCD_PRINT
lcd.noCursor();
#endif
Run = 2; //Toggle flag
}
else //Turn on
{
#ifdef LCD_PRINT
lcd.cursor();
#endif
Run = 1; //Toggle flag
}
}
}
}
}
if (Mode > 0) //Cursor enabled
{
#ifdef LCD_PRINT
lcd.noCursor();
#endif
}
return Flag;
}
void ShowFail(void)
{
lcd_fixed_string(Failed1_str); //Display: No component
lcd_line(2); //Move to line #2
lcd_fixed_string(Failed2_str); //Display: found!
if (Check.Diodes > 0) //Diodes found
{
lcd_space(); //Display space
lcd_data(Check.Diodes + '0'); //Display number of diodes found
lcd_fixed_string(Diode_AC_str); //Display: -|>|-
}
RunsMissed++; //Increase counter
RunsPassed = 0; //Reset counter
}
void ShowError()
{
if (Check.Type == TYPE_DISCHARGE) //Discharge failed
{
lcd_fixed_string(DischargeFailed_str); //Display: Battery?
lcd_line(2);
lcd_testpin(Check.Probe);
lcd_data(':');
lcd_space();
DisplayValue(Check.U, -3, 'V');
}
}
void ShowDiode_Uf(Diode_Type *Diode)
{
if (Diode == NULL) return;
DisplayValue(Diode->V_f, -3, 'V');
}
void ShowDiode_C(Diode_Type *Diode)
{
if (Diode == NULL) return;
MeasureCap(Diode->C, Diode->A, 0);
DisplayValue(Caps[0].Value, Caps[0].Scale, 'F');
}
void ShowDiode(void)
{
Diode_Type *D1; //Pointer to diode #1
Diode_Type *D2 = NULL; //Pointer to diode #2
byte SkipFlag = 0; //Flag for anti-parallel diodes
byte A = 5; //ID of common anode
byte C = 5; //ID of common cothode
unsigned int I_leak; //Leakage current
D1 = &Diodes[0]; //Pointer to first diode
if (Check.Diodes == 1) //Single diode
{
C = D1->C; //Make anode first pin
}
else if (Check.Diodes == 2) //Two diodes
{
D2 = D1;
D2++; //Pointer to second diode
if (D1->A == D2->A) //Common anode
{
A = D1->A; //Save common anode
}
else if (D1->C == D2->C) //Common cathode
{
C = D1->C; //Save common cathode
}
else if ((D1->A == D2->C) && (D1->C == D2->A))
{
A = D1->A; //Anode and cathode
C = A; //Are the same
SkipFlag = 1; //Signal anti-parallel diodes
}
}
else if (Check.Diodes == 3) //Three diodes
{
byte n;
byte m;
for (n = 0; n <= 2; n++) //Loop for first diode
{
D1 = &Diodes[n]; //Get pointer of first diode
for (m = 0; m <= 2; m++) //Loop for second diode
{
D2 = &Diodes[m]; //Get pointer of second diode
if (n != m) //Don't check same diode :-)
{
if (D1->C == D2->A) //Got match
{
n = 5; //End loops
m = 5;
}
}
}
}
if (n < 5) D2 = NULL; //No match found
C = D1->C; //Cathode of first diode
A = 3; //In series mode
}
else //To much diodes
{
D1 = NULL; //Don't display any diode
ShowFail(); //And tell user
return;
}
if (A < 3) lcd_testpin(D1->C); //Common anode
else lcd_testpin(D1->A); //Common cathode
if (A < 3) lcd_fixed_string(Diode_CA_str); //Common anode
else lcd_fixed_string(Diode_AC_str); //Common cathode
if (A < 3) lcd_testpin(A); //Common anode
else lcd_testpin(C); //Common cathode
if (D2) //Second diode
{
if (A <= 3) lcd_fixed_string(Diode_AC_str); //Common anode or in series
else lcd_fixed_string(Diode_CA_str); //Common cathode
if (A == C) lcd_testpin(D2->A); //Anti parallel
else if (A <= 3) lcd_testpin(D2->C); //Common anode or in series
else lcd_testpin(D2->A); //Common cathode
}
lcd_line(2); //Go to line #2
lcd_fixed_string(Vf_str); //Display: Vf=
ShowDiode_Uf(D1); //First diode
lcd_space();
if (D2 == NULL) //Single diode
{
if (D1->V_f2 < 250)
{
lcd_data('(');
DisplayValue(D1->V_f2, 0, 0);
lcd_data(')');
}
UpdateProbes(D1->C, D1->A, 0); //Reverse diode
I_leak = GetLeakageCurrent(); //Get current (in A‚ÂlA)
if (I_leak > 0) //Show if not zero
{
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Next page
#else
delay(3000);
#endif
lcd_clear_line(2); //Only change line #2
lcd_fixed_string(I_R_str); //Display: I_R=
DisplayValue(I_leak, -6, 'A'); //Display current
}
}
else
{
ShowDiode_Uf(D2); //Second diode (optional)
}
if (SkipFlag == 0)
{
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Next page
#else
delay(3000);
#endif
lcd_clear_line(2); //Only change line #2
lcd_fixed_string(DiodeCap_str); //Display: C=
ShowDiode_C(D1); //First diode
lcd_space();
ShowDiode_C(D2); //Second diode (optional)
}
}
void ShowBJT(void)
{
Diode_Type *Diode; //Pointer to diode
unsigned char *String; //Display string pointer
byte Counter; //Counter
byte A_Pin; //Pin acting as anode
byte C_Pin; //Pin acting as cathode
unsigned int V_BE; //V_BE
signed int Slope; //Slope of forward voltage
if (Check.Type == TYPE_NPN) //NPN
String = (unsigned char *)NPN_str;
else //PNP
String = (unsigned char *)PNP_str;
lcd_fixed_string(String); //Display: NPN / PNP
if (Check.Diodes > 2) //Transistor is a set of two diodes :-)
{
lcd_space();
if (Check.Type == TYPE_NPN) //NPN
String = (unsigned char *)Diode_AC_str;
else //PNP
String = (unsigned char *)Diode_CA_str;
lcd_fixed_string(String); //Display: -|>|- / -|<|-
}
lcd_space();
lcd_fixed_string(EBC_str); //Display: EBC=
lcd_testpin(BJT.E); //Display emitter pin
lcd_testpin(BJT.B); //Display base pin
lcd_testpin(BJT.C); //Display collector pin
lcd_line(2); //Move to line #2
lcd_fixed_string(hFE_str); //Display: h_FE=
DisplayValue(BJT.hFE, 0, 0);
Diode = &Diodes[0]; //Get pointer of first diode
Counter = 0;
while (Counter < Check.Diodes) //Check all diodes
{
if (Check.Type == TYPE_NPN) //NPN
{
A_Pin = BJT.B;
C_Pin = BJT.E;
}
else //PNP
{
A_Pin = BJT.E;
C_Pin = BJT.B;
}
if ((Diode->A == A_Pin) && (Diode->C == C_Pin))
{
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Next page
#else
delay(3000);
#endif
lcd_clear_line(2); //Update line #2
lcd_fixed_string(V_BE_str); //Display: V_BE=
Slope = Diode->V_f - Diode->V_f2;
Slope /= 3;
if (BJT.hFE < 100) //Low hFE
{
V_BE = Diode->V_f;
}
else if (BJT.hFE < 250) //Mid-range hFE
{
V_BE = Diode->V_f - Slope;
}
else //High hFE
{
V_BE = Diode->V_f2 + Slope;
}
DisplayValue(V_BE, -3, 'V');
if (BJT.I_CE0 > 0) //Show if not zero
{
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Next page
#else
delay(3000);
#endif
lcd_clear_line(2); //Only change line #2
lcd_fixed_string(I_CEO_str); //Display: I_CE0=
DisplayValue(BJT.I_CE0, -6, 'A'); //Display current
}
Counter = Check.Diodes; //End loop
}
else
{
Counter++; //Increase counter
Diode++; //Next one
}
}
}
void Show_FET_IGBT_Extras(byte Symbol)
{
if (Check.Diodes > 0)
{
lcd_space(); //Display space
lcd_data(Symbol); //Display diode symbol
}
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Next page
#else
delay(3000);
#endif
lcd_clear();
lcd_fixed_string(Vth_str); //Display: Vth
DisplayValue(FET.V_th, -3, 'V'); //Display V_th in mV
lcd_line(2);
//Display gate capacitance
lcd_fixed_string(GateCap_str); //Display: Cgs=
MeasureCap(FET.G, FET.S, 0); //Measure capacitance
DisplayValue(Caps[0].Value, Caps[0].Scale, 'F');
}
void ShowFET(void)
{
byte Data; //Temp. data
byte Symbol; //Intrinsic diode
if (Check.Type & TYPE_N_CHANNEL) //n-channel
{
Data = 'N';
Symbol = LCD_CHAR_DIODE2; // '|<|' cathode pointing to drain
}
else //p-channel
{
Data = 'P';
Symbol = LCD_CHAR_DIODE1; // '|>|' cathode pointing to source
}
if (Check.Type & TYPE_MOSFET) //MOSFET
lcd_fixed_string(MOS_str); //Display: MOS
else //JFET
lcd_data('J'); //Display: J
lcd_fixed_string(FET_str); //Display: FET
lcd_space();
lcd_data(Data); //Display: N / P
lcd_fixed_string(Channel_str); //Display: -ch
if (Check.Type & TYPE_MOSFET) //MOSFET
{
lcd_space();
if (Check.Type & TYPE_ENHANCEMENT) //Enhancement mode
lcd_fixed_string(Enhancement_str);
else //Depletion mode
lcd_fixed_string(Depletion_str);
}
lcd_line(2); //Move to line #2
lcd_fixed_string(GDS_str); //Display: GDS=
lcd_testpin(FET.G); //Display gate pin
if (Check.Type & TYPE_JFET)
{
lcd_data('?');
lcd_data('?');
}
else
{
lcd_testpin(FET.D); //Display drain pin
lcd_testpin(FET.S); //Display source pin
}
if (Check.Type & (TYPE_ENHANCEMENT | TYPE_MOSFET))
{
Show_FET_IGBT_Extras(Symbol);
}
}
void ShowIGBT(void)
{
byte Data; //Temp. data
byte Symbol; //Intrinsic diode
if (Check.Type & TYPE_N_CHANNEL) //n-channel
{
Data = 'N';
Symbol = LCD_CHAR_DIODE2; // '|<|' cathode pointing to drain
}
else //p-channel
{
Data = 'P';
Symbol = LCD_CHAR_DIODE1; // '|>|' cathode pointing to source
}
lcd_fixed_string(IGBT_str); //Display: IGBT
lcd_space();
lcd_data(Data); //Display: N / P
lcd_fixed_string(Channel_str); //Display: -ch
lcd_space();
if (Check.Type & TYPE_ENHANCEMENT) //Enhancement mode
lcd_fixed_string(Enhancement_str);
else //Depletion mode
lcd_fixed_string(Depletion_str);
lcd_line(2); //Move to line #2
lcd_fixed_string(GCE_str); //Display: GCE=
lcd_testpin(FET.G); //Display gate pin
lcd_testpin(FET.D); //Display collector pin
lcd_testpin(FET.S); //Display emitter pin
Show_FET_IGBT_Extras(Symbol);
}
void ShowSpecial(void)
{
if (Check.Found == COMP_THYRISTOR)
{
lcd_fixed_string(Thyristor_str); //Display: thyristor
}
else if (Check.Found == COMP_TRIAC)
{
lcd_fixed_string(Triac_str); //Display: triac
}
lcd_line(2); //Move to line #2
lcd_fixed_string(GAK_str); //Display: GAK
lcd_testpin(BJT.B); //Display gate pin
lcd_testpin(BJT.C); //Display anode pin
lcd_testpin(BJT.E); //Display cathode pin
}
void ShowResistor(void)
{
Resistor_Type *R1; //Pointer to resistor #1
Resistor_Type *R2; //Pointer to resistor #2
byte Pin; //ID of common pin
R1 = &Resistors[0]; //Pointer to first resistor
if (Check.Resistors == 1) //Single resistor
{
R2 = NULL; //Disable second resistor
Pin = R1->A; //Make B the first pin
}
else //Multiple resistors
{
R2 = R1;
R2++; //Pointer to second resistor
if (Check.Resistors == 3) //Three resistors
{
Resistor_Type *Rmax; //Pointer to largest resistor
Rmax = R1; //Starting point
for (Pin = 1; Pin <= 2; Pin++)
{
if (CmpValue(R2->Value, R2->Scale, Rmax->Value, Rmax->Scale) == 1)
{
Rmax = R2; //Update largest one
}
R2++; //Next one
}
if (R1 == Rmax) R1++;
R2 = R1;
R2++;
if (R2 == Rmax) R2++;
}
if ((R1->A == R2->A) || (R1->A == R2->B)) Pin = R1->A;
else Pin = R1->B;
}
if (R1->A != Pin) lcd_testpin(R1->A);
else lcd_testpin(R1->B);
lcd_fixed_string(Resistor_str);
lcd_testpin(Pin);
if (R2) //Second resistor
{
lcd_fixed_string(Resistor_str);
if (R2->A != Pin) lcd_testpin(R2->A);
else lcd_testpin(R2->B);
}
lcd_line(2);
DisplayValue(R1->Value, R1->Scale, LCD_CHAR_OMEGA);
if (R2) //Second resistor
{
lcd_space();
DisplayValue(R2->Value, R2->Scale, LCD_CHAR_OMEGA);
}
else //Single resistor
{
if (MeasureInductor(R1) == 1)
{
lcd_space();
DisplayValue(Inductor.Value, Inductor.Scale, 'H');
}
}
}
void ShowCapacitor(void)
{
Capacitor_Type *MaxCap; //Pointer to largest cap
Capacitor_Type *Cap; //Pointer to cap
byte Counter; //Loop counter
MaxCap = &Caps[0]; //Pointer to first cap
Cap = MaxCap;
for (Counter = 1; Counter <= 2; Counter++)
{
Cap++; //Next cap
if (CmpValue(Cap->Value, Cap->Scale, MaxCap->Value, MaxCap->Scale) == 1)
{
MaxCap = Cap;
}
}
lcd_testpin(MaxCap->A); //Display pin #1
lcd_fixed_string(Cap_str); //Display capacitor symbol
lcd_testpin(MaxCap->B); //Display pin #2
lcd_line(2); //Move to line #2
DisplayValue(MaxCap->Value, MaxCap->Scale, 'F');
}
void LoadAdjust(void)
{
if (EEPROM.read(10) == 126)
{
ReadEEP();
}
else
{
Config.RiL = R_MCU_LOW;
Config.RiH = R_MCU_HIGH;
Config.RZero = R_ZERO;
Config.CapZero = C_ZERO;
Config.RefOffset = UREF_OFFSET;
Config.CompOffset = COMPARATOR_OFFSET;
SaveEEP();
}
}
byte SelfTest(void)
{
byte Flag = 0; //Return value
byte Test = 1; //Test counter
byte Counter; //Loop counter
byte DisplayFlag; //Display flag
unsigned int Val0; //Voltage/value
signed int Val1 = 0, Val2 = 0, Val3 = 0;
ShortCircuit(1); //Make sure all probes are shorted
while (Test <= 6)
{
Counter = 1;
while (Counter <= 5)
{
lcd_clear();
lcd_data('T'); //Display: T
lcd_data('0' + Test); //Display test number
lcd_space();
DisplayFlag = 1; //Display values by default
switch (Test)
{
case 1: //Reference voltage
Val0 = ReadU(0x0e); //Dummy read for bandgap stabilization
Val0 = ReadU(0x0e); //Read bandgap reference voltage
lcd_fixed_string(URef_str); //Display: Vref
lcd_line(2);
DisplayValue(Val0, -3, 'V'); //Display voltage in mV
DisplayFlag = 0; //Reset flag
break;
case 2: //Compare Rl resistors (probes still shorted)
lcd_fixed_string(Rl_str); //Display: +Rl-
lcd_space();
lcd_fixed_string(ProbeComb_str); //Display: 12 13 23
R_PORT = 1 << (TP1 * 2);
R_DDR = (1 << (TP1 * 2)) | (1 << (TP2 * 2));
Val1 = ReadU_20ms(TP3);
Val1 -= ((long)UREF_VCC * (R_MCU_LOW + R_LOW)) / (R_MCU_LOW + R_LOW + R_LOW + R_MCU_HIGH);
//TP1: Gnd -- Rl -- probe-3 -- probe-1 -- Rl -- Vcc
R_DDR = (1 << (TP1 * 2)) | (1 << (TP3 * 2));
Val2 = ReadU_20ms(TP2);
Val2 -= ((long)UREF_VCC * (R_MCU_LOW + R_LOW)) / (R_MCU_LOW + R_LOW + R_LOW + R_MCU_HIGH);
R_PORT = 1 << (TP2 * 2);
R_DDR = (1 << (TP2 * 2)) | (1 << (TP3 * 2));
Val3 = ReadU_20ms(TP2);
Val3 -= ((long)UREF_VCC * (R_MCU_LOW + R_LOW)) / (R_MCU_LOW + R_LOW + R_LOW + R_MCU_HIGH);
break;
case 3: //Compare Rh resistors (probes still shorted)
lcd_fixed_string(Rh_str); //Display: +Rh-
lcd_space();
lcd_fixed_string(ProbeComb_str); //Display: 12 13 23
R_PORT = 2 << (TP1 * 2);
R_DDR = (2 << (TP1 * 2)) | (2 << (TP2 * 2));
Val1 = ReadU_20ms(TP3);
Val1 -= (UREF_VCC / 2);
R_DDR = (2 << (TP1 * 2)) | (2 << (TP3 * 2));
Val2 = ReadU_20ms(TP2);
Val2 -= (UREF_VCC / 2);
R_PORT = 2 << (TP2 * 2);
R_DDR = (2 << (TP2 * 2)) | (2 << (TP3 * 2));
Val3 = ReadU_20ms(TP1);
Val3 -= (UREF_VCC / 2);
break;
case 4: //Un-short probes
ShortCircuit(0); //Make sure probes are not shorted
Counter = 100; //Skip test
DisplayFlag = 0; //Reset flag
break;
case 5: //Rh resistors pulled down
lcd_fixed_string(RhLow_str); //Display: Rh-
R_PORT = 0;
R_DDR = 2 << (TP1 * 2);
Val1 = ReadU_20ms(TP1);
R_DDR = 2 << (TP2 * 2);
Val2 = ReadU_20ms(TP2);
R_DDR = 2 << (TP3 * 2);
Val3 = ReadU_20ms(TP3);
break;
case 6: //Rh resistors pulled up
lcd_fixed_string(RhHigh_str); //Display: Rh+
R_DDR = 2 << (TP1 * 2);
R_PORT = 2 << (TP1 * 2);
Val1 = ReadU_20ms(TP1);
R_DDR = 2 << (TP2 * 2);
R_PORT = 2 << (TP2 * 2);
Val2 = ReadU_20ms(TP2);
R_DDR = 2 << (TP3 * 2);
R_PORT = 2 << (TP3 * 2);
Val3 = ReadU_20ms(TP3);
break;
}
R_DDR = 0; //Input mode
R_PORT = 0; //All pins low
if (DisplayFlag)
{
lcd_line(2); //Move to line #2
DisplaySignedValue(Val1, 0 , 0); //Display TP1
lcd_space();
DisplaySignedValue(Val2, 0 , 0); //Display TP2
lcd_space();
DisplaySignedValue(Val3, 0 , 0); //Display TP3
}
if (Counter < 100) //When we don't skip this test
{
#ifdef BUTTON_INST
DisplayFlag = TestKey(1000, 0); //Catch key press or timeout
#else
delay(1000);
DisplayFlag = 0;
#endif
if (DisplayFlag > 0)
{
Counter = 100; //Skip current test anyway
if (DisplayFlag == 2) Test = 100; //Also skip selftest
}
}
Counter++; //Next run
}
Test++; //Next one
}
Flag = 1; //Signal success
return Flag;
}
byte SelfAdjust(void)
{
byte Flag = 0; //Return value
byte Test = 1; //Test counter
byte Counter; //Loop counter
byte DisplayFlag; //Display flag
unsigned int Val1 = 0, Val2 = 0, Val3 = 0;
byte CapCounter = 0; //Number of C_Zero measurements
unsigned int CapSum = 0; //Sum of C_Zero values
byte RCounter = 0; //Number of R_Zero measurements
unsigned int RSum = 0; //Sum of R_Zero values
byte RiL_Counter = 0; //Number of U_RiL measurements
unsigned int U_RiL = 0; //Sum of U_RiL values
byte RiH_Counter = 0; //Number of U_RiH measurements
unsigned int U_RiH = 0; //Sum of U_RiL values
unsigned long Val0; //Temp. value
ShortCircuit(1); //Make sure all probes are shorted
while (Test <= 5)
{
Counter = 1;
while (Counter <= 5)
{
lcd_clear();
lcd_data('A'); //Display: a
lcd_data('0' + Test); //Display number
lcd_space();
DisplayFlag = 1; //Display values by default
switch (Test)
{
case 1: //Resistance of probe leads (probes shorted)
lcd_fixed_string(ROffset_str); //Display: R0
lcd_space();
lcd_fixed_string(ProbeComb_str); //Display: 12 13 23
UpdateProbes(TP2, TP1, 0);
Val1 = SmallResistor(0);
if (Val1 < 100) //Within limit
{
RSum += Val1;
RCounter++;
}
UpdateProbes(TP3, TP1, 0);
Val2 = SmallResistor(0);
if (Val2 < 100) //Whithin limit
{
RSum += Val2;
RCounter++;
}
UpdateProbes(TP3, TP2, 0);
Val3 = SmallResistor(0);
if (Val3 < 100) //Within limit
{
RSum += Val3;
RCounter++;
}
break;
case 2: //Un-short probes
ShortCircuit(0); //Make sure probes are not shorted
Counter = 100; //Skip test
DisplayFlag = 0; //Reset display flag
break;
case 3: //Internal resistance of A‚ÂlC in pull-down mode
lcd_fixed_string(RiLow_str); //Display: Ri-
SetADCLow();
ADC_DDR = 1 << TP1;
R_PORT = 1 << (TP1 * 2);
R_DDR = 1 << (TP1 * 2);
Val1 = ReadU_5ms(TP1);
U_RiL += Val1;
ADC_DDR = 1 << TP2;
R_PORT = 1 << (TP2 * 2);
R_DDR = 1 << (TP2 * 2);
Val2 = ReadU_5ms(TP2);
U_RiL += Val2;
ADC_DDR = 1 << TP3;
R_PORT = 1 << (TP3 * 2);
R_DDR = 1 << (TP3 * 2);
Val3 = ReadU_5ms(TP3);
U_RiL += Val3;
RiL_Counter += 3;
break;
case 4: //Internal resistance of A‚ÂlC in pull-up mode
lcd_fixed_string(RiHigh_str); //Display: Ri+
R_PORT = 0;
ADC_PORT = 1 << TP1;
ADC_DDR = 1 << TP1;
R_DDR = 1 << (TP1 * 2);
Val1 = UREF_VCC - ReadU_5ms(TP1);
U_RiH += Val1;
ADC_PORT = 1 << TP2;
ADC_DDR = 1 << TP2;
R_DDR = 1 << (TP2 * 2);
Val2 = UREF_VCC - ReadU_5ms(TP2);
U_RiH += Val2;
ADC_PORT = 1 << TP3;
ADC_DDR = 1 << TP3;
R_DDR = 1 << (TP3 * 2);
Val3 = UREF_VCC - ReadU_5ms(TP3);
U_RiH += Val3;
RiH_Counter += 3;
break;
case 5: //Capacitance offset (PCB and probe leads)
lcd_fixed_string(CapOffset_str); //Display: C0
lcd_space();
lcd_fixed_string(ProbeComb_str); //Display: 12 13 23
MeasureCap(TP2, TP1, 0);
Val1 = (unsigned int)Caps[0].Raw;
if ((Caps[0].Scale == -12) && (Caps[0].Raw <= 100))
{
CapSum += Val1;
CapCounter++;
}
MeasureCap(TP3, TP1, 1);
Val2 = (unsigned int)Caps[1].Raw;
if ((Caps[1].Scale == -12) && (Caps[1].Raw <= 100))
{
CapSum += Val2;
CapCounter++;
}
MeasureCap(TP3, TP2, 2);
Val3 = (unsigned int)Caps[2].Raw;
if ((Caps[2].Scale == -12) && (Caps[2].Raw <= 100))
{
CapSum += Val3;
CapCounter++;
}
break;
}
SetADCHiz(); //Input mode
SetADCLow(); //All pins low
R_DDR = 0; //Input mode
R_PORT = 0; //All pins low
if (DisplayFlag)
{
lcd_line(2); //Move to line #2
DisplayValue(Val1, 0 , 0); //Display TP1
lcd_space();
DisplayValue(Val2, 0 , 0); //Display TP2
lcd_space();
DisplayValue(Val3, 0 , 0); //Display TP3
}
if (Counter < 100) //When we don't skip this test
{
#ifdef BUTTON_INST
DisplayFlag = TestKey(1000, 0); //Catch key press or timeout
#else
delay(1000);
DisplayFlag = 0;
#endif
if (DisplayFlag > 0)
{
Counter = 100; //Skip current test anyway
if (DisplayFlag == 2) Test = 100; //Also skip selftest
}
}
Counter++; //Next run
}
Test++; //Next one
}
if (CapCounter == 15)
{
Config.CapZero = CapSum / CapCounter;
Flag++;
}
if (RCounter == 15)
{
Config.RZero = RSum / RCounter;
Flag++;
}
if ((RiL_Counter == 15) && (RiH_Counter == 15))
{
U_RiL /= 5; //Average sum of 3 U_RiL
U_RiH /= 5; //Average sum of 3 U_RiH
Val1 = (UREF_VCC * 3) - U_RiL - U_RiH; //U_Rl * 3
Val0 = ((unsigned long)R_LOW * 100 * U_RiL) / Val1;
Val0 += 5; //For automagic rounding
Val0 /= 10; //Scale down to 0.1 Ohm
if (Val0 < 250UL) // < 25 Ohms
{
Config.RiL = (unsigned int)Val0;
Flag++;
}
Val0 = ((unsigned long)R_LOW * 100 * U_RiH) / Val1;
Val0 += 5; //For automagic rounding
Val0 /= 10; //Scale down to 0.1 Ohm
if (Val0 < 280UL) // < 29 Ohms
{
Config.RiH = (unsigned int)Val0;
Flag++;
}
}
ShowAdjust();
if (Flag == 4) Flag = 1; //All adjustments done -> success
else Flag = 0; //Signal error
return Flag;
}
void ShowAdjust(void)
{
lcd_clear();
lcd_fixed_string(RiLow_str); //Display: Ri-
lcd_space();
DisplayValue(Config.RiL, -1, LCD_CHAR_OMEGA);
lcd_line(2);
lcd_fixed_string(RiHigh_str); //Display: Ri+
lcd_space();
DisplayValue(Config.RiH, -1, LCD_CHAR_OMEGA);
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Let the user read
#else
delya(3000);
#endif
lcd_clear();
lcd_fixed_string(CapOffset_str); //Display: C0
lcd_space();
DisplayValue(Config.CapZero, -12, 'F'); //Display C0 offset
lcd_line(2);
lcd_fixed_string(ROffset_str); //Display: R0
lcd_space();
DisplayValue(Config.RZero, -2, LCD_CHAR_OMEGA);//Display R0
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Let the user read
#else
delay(3000);
#endif
lcd_clear();
lcd_fixed_string(URef_str); //Display: Vref
lcd_space();
DisplaySignedValue(Config.RefOffset, -3, 'V');
lcd_line(2);
lcd_fixed_string(CompOffset_str); //Display: AComp
lcd_space();
DisplaySignedValue(Config.CompOffset, -3, 'V');
#ifdef BUTTON_INST
TestKey(USER_WAIT, 11); //Let the user read
#else
delay(3000);
#endif
}
void PWM_Tool(unsigned int Frequency)
{
byte Test = 1; //Loop control and user feedback
byte Ratio; //PWM ratio
byte Prescaler; //Timer prescaler
unsigned int Top; //Top value
unsigned int Toggle; //Counter value to toggle output
uint32_t Value; //Temporary value
ShortCircuit(0); //Make sure probes are not shorted
lcd_clear();
lcd_fixed_string(PWM_str); //Display: PWM
lcd_data(' ');
DisplayValue(Frequency, 0, 'H'); //Display frequency
lcd_data('z'); //Make it Hz :-)
R_PORT = 0; //Make probe #1 and #3 ground
R_DDR = (1 << (TP1 * 2)) | (1 << (TP2 * 2)) | (1 << (TP3 * 2));
Value = CPU_FREQ / 2;
Value /= Frequency;
if (Value > 2000000) //Low frequency
{
Value /= 256;
Prescaler = (1 << CS12); //256
}
else if (Value > 16000) //Mid-range frequency
{
Value /= 64;
Prescaler = (1 << CS11) | (1 << CS10); //64
}
else //High frequency
{
Prescaler = (1 << CS10); //1
}
Top = (unsigned int)Value;
Ratio = 50; //Default ratio is 50%
Toggle = (Top / 2) - 1; //Compare value for 50%
Config.SleepMode = SLEEP_MODE_IDLE; //Change sleep mode to Idle
TCCR1B = 0; //Disable timer
TCCR1A = (1 << WGM11) | (1 << WGM10) | (1 << COM1B1);
TCCR1B = (1 << WGM13);
TCNT1 = 0; //Set counter to 0
OCR1A = Top - 1; //Set top value (-1)
OCR1B = Toggle; //Set value to compare with
TCCR1B = (1 << WGM13) | Prescaler;
while (Test > 0)
{
lcd_clear_line(2);
DisplayValue(Ratio, 0, '%'); //Show ratio in %
delay(500); //Smooth UI
#ifdef BUTTON_INST
Test = TestKey(0, 0); //Wait for user feedback
#else
delay(3000);
Test = 1;
#endif
if (Test == 1) //Short key press
{
delay(50); //Debounce button a little bit longer
#ifdef BUTTON_INST
Prescaler = TestKey(200, 0); //Check for second key press
#else
delay(3000);
Prescaler = 0;
#endif
if (Prescaler > 0) //Second key press
{
Test = 0; //End loop
}
else //Single key press
{
if (Ratio <= 95) Ratio += 5; // +5% and limit to 100%
}
}
else //Long key press
{
if (Ratio >= 5) Ratio -= 5; // -5% and limit to 0%
}
Value = (uint32_t)Top * Ratio;
Value /= 100;
Toggle = (unsigned int)Value;
Toggle--;
OCR1B = Toggle; //Update compare value
}
TCCR1B = 0; //Disable timer
TCCR1A = 0; //Reset flags (also frees PB2)
R_DDR = 0; //Set HiZ mode
Config.SleepMode = SLEEP_MODE_PWR_SAVE; //Reset sleep mode to default
}
void SaveEEP(void)
{
EEPROMWriteInt(1, Config.RiL);
EEPROMWriteInt(3, Config.RiH);
EEPROMWriteInt(5, Config.RZero);
EEPROM.write(7, Config.CapZero);
delay(10);
EEPROM.write(8, Config.RefOffset);
delay(10);
EEPROM.write(9, Config.CompOffset);
delay(10);
EEPROM.write(10, 126); //Saved :-)
delay(10);
}
void ReadEEP(void)
{
Config.RiL = EEPROMReadInt(1);
Config.RiH = EEPROMReadInt(3);
Config.RZero = EEPROMReadInt(5);
Config.CapZero = EEPROM.read(7);
Config.RefOffset = EEPROM.read(8);
Config.CompOffset = EEPROM.read(9);
}
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
delay(10);
EEPROM.write(p_address + 1, highByte);
delay(10);
}
void MainMenu(void)
{
#ifdef DEBUG_PRINT
unsigned int Frequency; //Frequency for PWM Tool
boolean doexit = false; //Exit Menu Flag
do
{
boolean cmdexec = false; //CMD Exec Flag
Serial.println();
Serial.println(X("** MAIN MENU"));
Serial.println();
Serial.println(X(" 1) PWM"));
Serial.println(X(" 2) SelfTest"));
Serial.println(X(" 3) Adjust"));
Serial.println(X(" 4) Save"));
Serial.println(X(" 5) Show"));
Serial.println(X(" 6) Default"));
Serial.print(X(" 0) Exit >"));
do
{
if (Serial.available() > 0)
{
char inChar = Serial.read();
Serial.println(inChar);
switch ((byte)inChar - 48)
{
case 1: //Pwm Menu
Serial.println();
Frequency = selFreq();
Serial.println();
Serial.println(X("Info:"));
Serial.println(X(" Short Press +"));
Serial.println(X(" Long Press -"));
Serial.println(X(" Double Press Exit"));
PWM_Tool(Frequency);
Serial.println();
cmdexec = true;
break;
case 2: //Selftest
SelfTest();
Serial.println();
cmdexec = true;
break;
case 3: //Adjust
SelfAdjust();
Serial.println();
cmdexec = true;
break;
case 4: //Save
SaveEEP();
Serial.println();
cmdexec = true;
case 5: //Show
ShowAdjust();
Serial.println();
cmdexec = true;
break;
case 6: //Default Parameters
DefaultPar();
Serial.println();
cmdexec = true;
break;
case 0: //Exit
cmdexec = true;
doexit = true;
Serial.println();
Serial.println(X("Done. Exit"));
return;
default:
Serial.print(X(" >"));
cmdexec = false;
doexit = false;
}
}
} while (cmdexec == false);
} while (doexit == false);
#else
delay(800);
LcdMenu();
#endif
}
unsigned int selFreq(void)
{
boolean cmdexec = false; //CMD Exec Flag
Serial.println(X("Select Frequency:"));
for (int f; f < 8; f++)
{
Serial.print(X(" "));
Serial.print(f + 1);
Serial.print(X(") "));
DisplayValue(PWM_Freq_table[f], 0, 0);
Serial.println(X("Hz"));
}
Serial.print(X(" >"));
do
{
if (Serial.available() > 0)
{
char inChar = Serial.read();
byte selNum = (byte)inChar - 48;
if (selNum > 0 && selNum < 9)
{
Serial.println(inChar);
cmdexec = true;
return PWM_Freq_table[selNum - 1];
}
else
{
Serial.println(X(" >"));
cmdexec = false;
}
}
} while (cmdexec == false);
return 100;
}
void LcdMenu(void)
{
byte Flag = 1; //Control flag
byte Selected; //ID of selected item
byte ID; //ID of selected item
unsigned int Frequency; //PWM frequency
void *Menu[6];
//Setup menu
Menu[0] = (void *)PWM_str;
Menu[1] = (void *)Selftest_str;
Menu[2] = (void *)Adjustment_str;
Menu[3] = (void *)Save_str;
Menu[4] = (void *)Show_str;
Menu[5] = (void *)Default_str;
lcd_clear();
lcd_fixed_string(Select_str);
Selected = MenuTool(6, 1, Menu, NULL);
switch (Selected)
{
case 0: //PWM tool
lcd_clear();
lcd_fixed_string(PWM_str);
ID = MenuTool(8, 2, (void **)PWM_Freq_table, (unsigned char *)Hertz_str);
Frequency = PWM_Freq_table[ID];
PWM_Tool(Frequency); //And run PWM tool
break;
case 1: //Self test
Flag = SelfTest();
break;
case 2: //Self adjustment
Flag = SelfAdjust();
break;
case 3: //Save self adjument values
SaveEEP();
break;
case 4: //Show self adjument values
ShowAdjust();
break;
}
lcd_clear();
if (Flag == 1)
lcd_fixed_string(Done_str); //Display: done!
else
lcd_fixed_string(Error_str); //Display: error!
}
byte MenuTool(byte Items, byte Type, void *Menu[], unsigned char *Unit)
{
byte Selected = 0; //Return value / ID of selected item
byte Run = 1; //Loop control flag
byte n; //Temp value
void *Address; //Address of menu element
unsigned int Value; //Temp. value
Items--; //To match array counter
lcd_data(':'); //Whatever:
while (Run)
{
lcd_clear_line(2);
Address = &Menu[Selected]; //Get address of element
if (Type == 1) //Fixed string
{
lcd_fixed_string(*(unsigned char **)Address);
}
else
{
Value = PWM_Freq_table[Selected];
DisplayValue(Value, 0, 0);
}
if (Unit) //Optional fixed string
{
lcd_fixed_string(Unit);
}
delay(100); //Smooth UI
#ifdef LCD_PRINT
lcd.setCursor(15, 2);
#endif
if (Selected < Items) n = 126; //Another item follows
else n = 127; //Last item
lcd_data(n);
n = TestKey(0, 0); //Wait for testkey
if (n == 1) //Short key press: moves to next item
{
Selected++; //Move to next item
if (Selected > Items)
{
Selected = 0; //Roll over to first one
}
}
else if (n == 2) //Long key press: select current item
{
Run = 0;
}
}
lcd_clear();
delay(500); //Smooth UI
return Selected;
}
void DefaultPar(void)
{
Config.RiL = R_MCU_LOW;
Config.RiH = R_MCU_HIGH;
Config.RZero = R_ZERO;
Config.CapZero = C_ZERO;
Config.RefOffset = UREF_OFFSET;
Config.CompOffset = COMPARATOR_OFFSET;
SaveEEP();
}
Result:
If you want, you can use my PCB design file from here.
Conclusion:
This is very useful for a small range of components. This tester can test Resistors, capacitors, diodes, transistors, MOSFETs, UJT, batteries, inductors, logic gates, and so on.
I hope this project was helpful to you. If you make one for yourself, it will be a great pleasure for me. Anywhere you need help, let me know. Please share this project and subscribe to my blog. Thank you.
Liked this article? Subscribe to our newsletter:
or,
Visit LabProjectsBD.com for more inspiring projects and tutorials.
Thank you!
Check this out: 5 coolest multimeters you can buy




27 Comments
Asimiyu · 08/04/2021 at 3:10 pm
Wao!!!. You have done a very good job here. May God Almighty continue to reward you and the first person who initiate. It is not an easy task!. The programs code is too Bucky!!!. I can’t wait construct this, I will give feedback about it soon to test it.
MKDas · 10/04/2021 at 3:29 am
Thanks.
Asimiyu · 08/04/2021 at 3:28 pm
Sir, I think the red probe will be connected to pin labelled as “TESTER” and where the black probe would be connected to?. Ground?.
MKDas · 10/04/2021 at 3:30 am
Yes
Asimiyu · 10/04/2021 at 5:44 am
Ok, thanks Sir.
CNDA · 11/04/2021 at 6:27 am
Many thanks for this excellent project
MKDas · 11/04/2021 at 7:27 am
You are welcome
djalltra · 14/04/2021 at 1:49 pm
can you please provide the link to the original article on instructables
MKDas · 15/04/2021 at 8:25 am
I’ll post if I found again. A long time ago I found the original article.
khaled aly · 13/05/2021 at 11:19 am
HELLO SIR
THANK YOU ON THE PROJECT
BUT
I tried this project on the Proteus program, but it is not working well. Please advise me
MKDas · 13/05/2021 at 11:21 am
It will not work in proteus. You need to test in real life.
khaled aly · 14/05/2021 at 8:40 am
how adj fuse atmega328 tester
khaled aly · 14/05/2021 at 9:07 am
I built the whole project, but it does not work well. It gives value on the screen knowing that I do not put any component how can I adjust this project
MKDas · 16/05/2021 at 12:42 pm
The working principle of the project is not so efficient as a multi-meter. It is a simple project and being developed again by again one by one who made or modified this. The way of sensing capacitance used in this project is measuring wrong due to minor capacitances between circuit test terminals. It is advised earlier already. You can add some pull-down resistors of high values to solve this issue.
MKDas · 16/05/2021 at 12:39 pm
where is atmega328 tester?
khaled aly · 14/05/2021 at 4:16 pm
Peace be upon you
Please help with this project. I built this project, but it does not work well. If you press the test key, it gives a capacitor value, knowing that there is no component. I also need the fuses to adjust, even if in a video, I would be very thankful to you, Khaled.
MKDas · 16/05/2021 at 12:42 pm
Replied earlier.
khaled aly · 16/05/2021 at 12:09 pm
The answer is difficult for you, nor are there any projects, all of them are problems, and you say that they are complete
MKDas · 16/05/2021 at 12:44 pm
Before throwing any words it is a good practice to think yourself inside your mind again and again.
khaled aly · 16/05/2021 at 4:49 pm
First, I do not throw any words to anyone. Second, you said that the project is working well, and I told you that it does not work on the proteus, and you said it does not work on the proteus and the project must be built, but what happens to the proteus happens on the hardware, so what is the solution
thank you for help
khaled
MKDas · 17/05/2021 at 6:18 am
Proteus uses all the ideal properties of the parts. In practice, all parts have a tolerance. That is why there will be some changes in characteristics in real life. Proteus is good for code simulation. But based on my 10+ years of experience I can say that all circuits should be made in real life if it is not only the coding simulation.
khaled aly · 18/05/2021 at 6:59 pm
thank you for replying
But I actually applied the circuit, but there are some problems in it that I would like to solve
Otherwise, it put a file of its value in Henry, so it gives me another value. Is this normal, is there a control for the fuses, or is there a problem for me. Please ask any solution because I really need this project and thank you again for the response
Khaled Ali
MKDas · 19/05/2021 at 6:11 am
I mentioned very top of this project that the original code is written by someone else and it is being developed step by step by many peoples. I added some and corrected some. You may need to correct too. But from my experience, I can say that some minor issues come due to PCB traces and capacitance due to moisture. I tested it, found 80% correct to detect the device. But note that this is not a perfect way to detect or test the device parameters using this project. But it is pretty helpful for small ranges.
chandrasekar · 19/06/2021 at 3:09 pm
Hi i have created the circuit based on ur diagram. Whenever i want to check a capacitor it show “BATTERY? 1: 3699mV”. Pls tell how to setup the and check the components pls. Also pls make a video on that… I eargly waiting for your replies and videos
MKDas · 20/06/2021 at 5:27 am
Check for PCB shorts or leakage capacitance. A minor capacitance will generate wrong characteristics. That is why it is sensing wrong component.
khaled bashomil · 07/06/2022 at 2:41 pm
#error Invalid Parameters: Use LCD_PRINT or DEBUG_PRINT
MKDas · 08/06/2022 at 9:51 am
maybe you mistook putting the semiclone.