#include #include #include #include #include float computeMilliVolts(int16_t counts); #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) #define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); Adafruit_ADS1115 ads; /* Use this for the 16-bit version */ // These constants won't change. They're used to give names to the pins used: // constants won't change. They're used here to set pin numbers: const int emitterPin = 2; // arduino pin the emitter pin of the transistor is on const int basePin = 3; // arduino pin the base pin of the transistor is on const int collectorPin = 5; // arduino pin the collector pin of the transistor is on const int collectorResistorPin = 6; // arduino pin the collector resistor is on const int baseResistorPin = 7; // arduino pin the base resistor is on // variables will change: int collectorPinState = 0; // variable for reading the collector pin status int transistorType = 0; // Type 0 is NPN, Type 1 is PNP void setup() { int16_t adc0, adc1; // variables to hold the 16-bit ADC reading float collector_milliVolts = 0.0; // variable to hold the collector voltage in milli-volts float rail_milliVolts = 0.0; // variable to hold the rail voltage in milli-volts float leak_milliVolts = 0.0; // variable to hold leakage voltage in milli-volts float leak_uA = 0.0; // variable to hold leakage current in micro-amps float baseCurrent_uA = 0.0; // variable to hold the base current in micro-amps float gain = 0.0; // variable to hold the gain (which includes the leakage) float trueGain = 0.0; // variable to hold the true gain (subtracting leakage) // below are the results of resistance of the resistors used on this board. int16_t collector_resistor = 997; // collector resistor 1K is really 0.997K long base_resistor = 1204000; // base resistor 1.2M is really 1.204M // initialize serial communications at 9600 bps: Serial.begin(9600); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } // Show initial display buffer contents on the screen -- // the library initializes this with an Adafruit splash screen. display.display(); if (!ads.begin()) { Serial.println("Failed to initialize ADS."); while (1); } // In this step, we will set the emitter pin to GND and the base pin // to 5 volts. So, we need to set the emitter and base pins to output // mode, so we can connect them to those power rails. The collector // pin will need to be set to input, as we will use this to see if the // transistor is an NPN or PNP germanium transistor pinMode(emitterPin, OUTPUT); pinMode(basePin, OUTPUT); pinMode(collectorPin, INPUT); digitalWrite(emitterPin, LOW); // set the emitter pin to GND digitalWrite(basePin, HIGH); // set the base pin to 5V delay(50); // wait a moment // see what the voltage is on the collector pin collectorPinState = digitalRead(collectorPin); // based off if voltage was detected on the collector pin... if (collectorPinState == HIGH) { // if we saw the voltage from the base pin jump over to the collector pin, // it's a PNP transistor (type 1) transistorType = 1; } else { // otherwise, the voltage from the base pin did not jump to the collector // pin, it's an NPN transistor (type 0) transistorType = 0; // if it is a silicon PNP transistor, this will have the same effect as an NPN // so it doesn't work. } delay(50); // wait a moment // reset the emitter and base pins and set them back to floating digitalWrite(emitterPin, LOW); pinMode(emitterPin, INPUT); digitalWrite(basePin, LOW); pinMode(basePin, INPUT); delay(50); // wait a moment // Now that we know if we have an NPN or PNP transistors, we can begin to read leak // of the transistor. // if it is a PNP transistor, emitter goes to 5V and collector goes through the // collector resistor to ground. // if it is an NPN transistor, emitter goes to ground and collector goes through the // collector resistor to 5V. if(transistorType == 1) { // Transistor is PNP pinMode(emitterPin, OUTPUT); // Set emitter pin to output so that we can... digitalWrite(emitterPin, HIGH); // Emitter goes straight to 5V pinMode(collectorResistorPin, OUTPUT); digitalWrite(collectorResistorPin, LOW); // Collector goes to ground via collector resistor } else { // Transistor is NPN pinMode(emitterPin, OUTPUT); // Set emitter pin to output so that we can... digitalWrite(emitterPin, LOW); // Emitter goes straight to ground pinMode(collectorResistorPin, OUTPUT); digitalWrite(collectorResistorPin, HIGH); // Collector goes to 5V via collector resistor } // read in voltages from the 16-bit ADC adc0 = ads.readADC_SingleEnded(0); adc1 = ads.readADC_SingleEnded(1); // rail voltage goes into ADC0 and collector voltage goes into ADC1 rail_milliVolts = computeMilliVolts(adc0); collector_milliVolts = computeMilliVolts(adc1); // ohms law, V / R = I. Because we want current in uA and the volts are in milliVolts, // we need to multiply by 1000.0. This will give us the current from the base pin. // we have not applied base current at this point, but will do so later on. baseCurrent_uA = (rail_milliVolts * 1000.0) / base_resistor; if(transistorType == 1) { // Transistor is PNP // in case we get any weird jitter on the collector pin if(collector_milliVolts <= 0.1) { collector_milliVolts = 0.0; } // the leakage has caused an amount of collector voltage that we need to record and subtract // from the gain, when we get to that point. leak_milliVolts = collector_milliVolts; // ohms law, V / R = I. Because we want current in uA and the volts are in milliVolts, // we need to multiply by 1000.0. This will give us the current from the collector pin. leak_uA = (leak_milliVolts * 1000.0) / collector_resistor; // Output our findings thus far out the serial port Serial.println("PNP Transistor"); Serial.print("PWR Rail: "); Serial.print(rail_milliVolts); Serial.println("mV"); Serial.print("Collector: "); Serial.print(leak_milliVolts); Serial.println("mV"); Serial.print("Leak: "); Serial.print(leak_uA); Serial.println("uA"); Serial.print("Base: "); Serial.print(baseCurrent_uA); Serial.println("uA"); delay(50); // wait a moment // now we set the base pin to output and apply a small, but pre-calculated, current over it. pinMode(baseResistorPin, OUTPUT); digitalWrite(baseResistorPin, LOW); // Base goes to ground via base resistor because it is PNP // read in voltages from the 16-bit ADC adc0 = ads.readADC_SingleEnded(0); adc1 = ads.readADC_SingleEnded(1); // rail voltage goes into ADC0 and collector voltage goes into ADC1 rail_milliVolts = computeMilliVolts(adc0); collector_milliVolts = computeMilliVolts(adc1); // gain is collector-voltage / collector-resistance / base-current // so if we have 750 millivolts on the collector after applying 4uA of current to the base pin, // and we are using a 1K resistor for the collector resistor, the our gain calculation would look like this: // gain = (750mV / 1000) / 1000 ohms / (4uA / 1,000,000) // gain = 0.75V / 1000 ohms / 0.000004A // gain = 187.5 gain = (collector_milliVolts / 1000.0) / collector_resistor / (baseCurrent_uA / 1000000.0); // however, this isn't the true story. The leakage voltage also goes over the collector resistor, so we must subtract // the leakage voltage from the collector voltage. So, if we had a leakage voltage of 100mV (which with a 1K collector // resistor is 100uA of leakage current), we don't actually have 750 millivolts on the collector after applying the // 4uA of current to the base pin, but rather 750mV - 100mV = 650mV, so the true gain would calculate as: // gain = (650mV / 1000) / 1000 ohms / (4uA / 1,000,000) // gain = 0.65V / 1000 ohms / 0.000004A // gain = 162.5 trueGain = ((collector_milliVolts - leak_milliVolts) / 1000.0) / collector_resistor / (baseCurrent_uA / 1000000.0); // output our findings out the serial port Serial.print("Gain: "); Serial.print(gain); Serial.println(" hfe"); Serial.print("True Gain: "); Serial.print(trueGain); Serial.println(" hfe"); // clear the OLED display display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0,0); // Start at top-left corner display.println(F("PNP Transistor")); // Start printing to screen // we will continue the rest later } else { // Transistor is NPN // the leakage has caused an amount of collector voltage that we need to record and subtract // from the gain, when we get to that point. Because the leakage is in reference to the rail // voltage, as this is an NPN transistor, we must subtract from the rail voltage to get leak // voltage leak_milliVolts = rail_milliVolts - collector_milliVolts; // in case we get any weird jitter on the collector pin if(leak_milliVolts < 0.1) { leak_milliVolts = 0.0; } // ohms law, V / R = I. Because we want current in uA and the volts are in milliVolts, // we need to multiply by 1000.0. This will give us the current from the collector pin. leak_uA = (leak_milliVolts / collector_resistor) * 1000.0; Serial.println("NPN Transistor"); Serial.print("PWR Rail: "); Serial.print(rail_milliVolts); Serial.println("mV"); Serial.print("Collector: "); Serial.print(leak_milliVolts); Serial.println("mV"); Serial.print("Leak: "); Serial.print(leak_uA); Serial.println("uA"); Serial.print("Base: "); Serial.print(baseCurrent_uA); Serial.println("uA"); delay(50); // wait a moment // now we set the base pin to output and apply a small, but pre-calculated, current over it. pinMode(baseResistorPin, OUTPUT); digitalWrite(baseResistorPin, HIGH); // Base goes to 5V via base resistor because it is NPN // read in voltages from the 16-bit ADC adc0 = ads.readADC_SingleEnded(0); adc1 = ads.readADC_SingleEnded(1); // rail voltage goes into ADC0 and collector voltage goes into ADC1 rail_milliVolts = computeMilliVolts(adc0); collector_milliVolts = computeMilliVolts(adc1); // because the collector voltage is in reference to the rail voltage, we have to subtract from the rail voltage // gain is collector-voltage / collector-resistance / base-current // so if we have 750 millivolts on the collector after applying 4uA of current to the base pin, // and we are using a 1K resistor for the collector resistor, the our gain calculation would look like this: // gain = (750mV / 1000) / 1000 ohms / (4uA / 1,000,000) // gain = 0.75V / 1000 ohms / 0.000004A // gain = 187.5 gain = ((rail_milliVolts - collector_milliVolts) / 1000.0) / collector_resistor / (baseCurrent_uA / 1000000.0); // however, this isn't the true story. The leakage voltage also goes over the collector resistor, so we must subtract // the leakage voltage from the collector voltage. So, if we had a leakage voltage of 100mV (which with a 1K collector // resistor is 100uA of leakage current), we don't actually have 750 millivolts on the collector after applying the // 4uA of current to the base pin, but rather 750mV - 100mV = 650mV, so the true gain would calculate as: // gain = (650mV / 1000) / 1000 ohms / (4uA / 1,000,000) // gain = 0.65V / 1000 ohms / 0.000004A // gain = 162.5 trueGain = (((rail_milliVolts - collector_milliVolts) - leak_milliVolts) / 1000.0) / collector_resistor / (baseCurrent_uA / 1000000.0); Serial.print("Gain: "); Serial.print(gain); Serial.println(" hfe"); Serial.print("True Gain: "); Serial.print(trueGain); Serial.println(" hfe"); // clear the OLED display display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0,0); // Start at top-left corner display.println(F("NPN Transistor")); // Start printing to screen // we will continue the rest later } // Now display our gain and leakage findings display.print(F("Gain: ")); display.print(trueGain); display.println(F(" hfe")); display.print(F("Leak: ")); display.print(leak_uA); display.println(F("uA")); display.display(); // Then reset all pins back to input mode pinMode(emitterPin, INPUT); pinMode(basePin, INPUT); pinMode(collectorPin, INPUT); pinMode(collectorResistorPin, INPUT); pinMode(baseResistorPin, INPUT); // So that we can then set the pins back to floating digitalWrite(emitterPin, LOW); digitalWrite(basePin, LOW); digitalWrite(collectorPin, LOW); digitalWrite(collectorResistorPin, LOW); digitalWrite(baseResistorPin, LOW); } void loop() { // wait 500 milliseconds before the next loop for the analog-to-digital // converter to settle after the last reading: delay(500); } /**************************************************************************/ /*! @brief Returns true if conversion is complete, false otherwise. @param counts the ADC reading in raw counts @return the ADC reading in milli-volts */ /**************************************************************************/ float computeMilliVolts(int16_t counts) { uint8_t bitShift = 0; ///< bit shift amount return counts * 1000.0 * (6.144f / (32768 >> bitShift)); }