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:
Handling electricity carries inherent risks. It’s essential to have the appropriate skills to manage it safely. Proceed at your own risk, as the author disclaims responsibility for any misuse, harm, or errors. All content on this website is unique and copyrighted; please avoid unauthorized copying. While most articles are open-source for your benefit, feel free to use the knowledge provided. If you find our resources helpful, consider purchasing available materials to support our work.
For assistance or guidance, leave a comment below; the author is committed to helping. Some articles may contain affiliate links that support the author with a commission at no additional cost to you. Thank you for your understanding and support.
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.
For Professional Designs or Help:
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.