wareck il y a 2 ans
commit
b1524e2cf4
2 fichiers modifiés avec 367 ajouts et 0 suppressions
  1. 29 0
      README.md
  2. 338 0
      germanium_tester.ino

+ 29 - 0
README.md

@@ -0,0 +1,29 @@
+# arduino-uno-germanium-transistor-tester
+<p align="center">
+  Arduino IDE C code for the Arduino Uno, using a SSD1306 OLED display and an ADS1115 16-Bit ADC<br />
+  <img width="572" src="https://i.postimg.cc/q75xjL0X/20211112-223037.jpg" alt="Breadboard">
+</p>
+
+<p>I saw a post on DIY Stompboxes where someone else was attempting to do this, but I never saw them complete it</p>
+<p>I knew one of the problems was that the ADC on the ATMEGA328P is kind of limited and prone to too much noise</p>
+<p>So I found a breakout board for the ADS1115 16-Bit ADC that was available and cheap. So I opted to use that to overcome</p>
+<p>the native hardware limitations</p>
+
+<h2>Arduino Uno Germanium Transistor Tester</h2>
+
+<p align="center">
+  <img width="820" src="https://i.postimg.cc/G2SdRXwg/Image4.jpg" alt="Arduino Uno Germanium Transistor Tester schematic">
+</p>
+
+<p>&nbsp;</p>
+<h2>Arduino Uno Germanium Transistor Tester BOM</h2>
+<p>
+1x 1K ohm Metal Film resistor: <a href="https://www.digikey.com/en/products/detail/stackpole-electronics-inc/RNF14FTD1K00/1706678">https://www.digikey.com/en/products/detail/stackpole-electronics-inc/RNF14FTD1K00/1706678</a><br />
+1x 1.2M ohm Metal Film resistor: <a href="https://www.digikey.com/en/products/detail/stackpole-electronics-inc/RNF14FTD1M21/1750283"></a>https://www.digikey.com/en/products/detail/stackpole-electronics-inc/RNF14FTD1M21/1750283<br />
+1x 100uF Electrolytic Capacitor: <a href="https://www.digikey.com/en/products/detail/nichicon/UVR1H101MPD1TD/3438480">https://www.digikey.com/en/products/detail/nichicon/UVR1H101MPD1TD/3438480</a><br />
+1x 100nF Ceramic Capacitor: <a href="https://www.digikey.com/en/products/detail/vishay-beyschlag-draloric-bc-components/K104K15X7RF5TL2/286538">https://www.digikey.com/en/products/detail/vishay-beyschlag-draloric-bc-components/K104K15X7RF5TL2/286538</a><br />
+1x Arduino Nano: <a href="https://www.amazon.com/ALMOCN-Compatible-ATmega328P-Controller-Arduino/dp/B08HVPMLKG/">https://www.amazon.com/ALMOCN-Compatible-ATmega328P-Controller-Arduino/dp/B08HVPMLKG/</a><br />
+1x I2C SSD1306 OLED Display Module: <a href="https://www.amazon.com/ALMOCN-Module-Serial-Display-SSD1306/dp/B092C8LB7B/">https://www.amazon.com/ALMOCN-Module-Serial-Display-SSD1306/dp/B092C8LB7B/</a><br />
+1x I2C ADS1115 16-Bit ADC Module: <a href="https://www.amazon.com/TeOhk-Converter-Programmable-Amplifier-Development/dp/B081CJWGHZ/">https://www.amazon.com/TeOhk-Converter-Programmable-Amplifier-Development/dp/B081CJWGHZ/</a><br />
+</p>
+<p>Have fun!</p>

+ 338 - 0
germanium_tester.ino

@@ -0,0 +1,338 @@
+#include <Adafruit_ADS1X15.h>
+#include <SPI.h>
+#include <Wire.h>
+#include <Adafruit_GFX.h>
+#include <Adafruit_SSD1306.h>
+
+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));
+}