Archive for March 15th, 2012

MOSFET RDS Tester: First Light

Well, truth be known, it took a bit of tweaking to get to this point, but this was the first dependable & repeatable measurement:

BUZ71A-overview

BUZ71A-overview

Rescaling the graph to show just the interesting part down near the origin:

BUZ71A-detail

BUZ71A-detail

The VGS output steps from 4.0 to 10.0 V by 0.25 V, which is too fine until I get the Gnuplot script sorted out. The ID output runs from 0.0 A to 2.0 A in steps of 50 mA, which makes for smooth curves. These are all at 30 °C.

The drain resistance flattens out nicely for VGS beyond 7 V, which is well over the BUZ71A max threshold of 4.0 V. That means you really need more than the usual 5 V supply to control the thing; I’ll eventually try some “logic level” MOSFETs. Part of the trick will be to find a logic-level MOSFET with a relatively high drain resistance suitable for current sensing.

The board looks like this, with the foam shako for the thermal block and some MOSFET victims off to the side:

MOSFET RDS Tester - overview

MOSFET RDS Tester - overview

The key part of the schematic:

Schematic - MOSFET path

Schematic - MOSFET path

Two Arduino PWM outputs set the gate voltage and maximum drain current. The three jumpers near the middle allow various feedback paths, although the only one that really makes sense is closing the current loop. The trimpot is unused and the analog output directly sets the drain current limit at 0.5 A/V: 4 V → 2 A. The PWM outputs must run at 32 kHz, not the Arduino-standard 500-ish Hz.

The MAX4544 SPDT analog multiplexers switch between ground and the PWM voltages. That’s a simple way to turn the outputs off and on without waiting for the PWM values to ramp up and down. The LEDs on those control signals provide an indication that the firmware hasn’t fallen off the rails.

Three Arduino analog inputs report the drain voltage, actual drain current, and temperature input. The LM324 op amps run from ±12 V, so a pair of BAT54S dual diodes clamp the analog inputs at one Schottky diode drop below ground and above 5 V. That should be close enough to prevent any damage without rounding off the values near the extremes, given the fairly high op-amp output resistors; the analog inputs present a reasonably high impedance and it seems to not matter much.

The measuring sequence amounts to a pair of nested loops:

  • Step the gate voltage
  • Step the drain current limit

The inner loop ends when the current limit, the actual current, or the drain voltage exceeds the corresponding maximum value. The outer loop ends when the gate voltage exceeds its limit.

A 100 ms delay after changing any analog output allows time for the voltages to settle before taking the next set of inputs.

Each pass of the loop updates the PI loop controlling the thermal block temperature. That’s certainly sub-optimal, but works well enough for my simple needs.

The Arduino source code for the measurement loop:

void loop() {

    digitalWrite(PIN_HEARTBEAT,HIGH);           // show that we've arrived

//--- Stabilize temperature

    Temperature = ReadTemperature();
    SetPeltier(Temperature,TSetpoint);

    if (abs(Temperature - TSetpoint) > T_ACCEPT) {

      Serial.print("# Exceed T limit: ");
      Serial.print(Temperature,1);
      Serial.print(" C ");

      while (abs(Temperature - TSetpoint) > T_DEADBAND) {
        Temperature = ReadTemperature();
        SetPeltier(Temperature,TSetpoint);
        TogglePin(PIN_HEARTBEAT);
        delay(SETTLING_TIME);
        Serial.print('.');
      }
      Serial.print(" Now at: ");
      Serial.print(Temperature,1);
      Serial.println(" C");
    }

//--- Record current data point

    IDrainSense = GetIDrain();
    VDrainSense = GetVDrain();

    Serial.print(VGateSet,3);
    Serial.print('\t');
    Serial.print(VDrainSense,3);
    Serial.print('\t');
    Serial.print(IDrainSense,3);
    Serial.print('\t');
    Serial.print((IDrainSense == 0.0) ? 0.0 : (VDrainSense / IDrainSense),3);
    Serial.print('\t');
    Serial.print(Temperature,1);
    Serial.print('\t');
    Serial.print(millis() - StartTime);
    Serial.println();

//--- Step to next point

    if ((IDrainLimit > MAX_DRAIN_CURRENT) ||        // beyond last current increment
        (IDrainSense > MAX_DRAIN_CURRENT) ||        // power supply current limit
        (VDrainSense > MAX_DRAIN_VOLTAGE)) {        // beyond linear voltage measurement
      IDrainLimit = 0.0;
      VGateSet += VGATE_STEP;
      if (VGateSet <= MAX_GATE_VOLTAGE) {
        PrintHeader();
      }
    }
    else {
      IDrainLimit += IDRAIN_STEP;
    }

    SetIDrain(IDrainLimit);
    SetVGate(VGateSet);

    TogglePin(PIN_HEARTBEAT);
    delay(SETTLING_TIME);                           // wait for settling

    if (VGateSet > MAX_GATE_VOLTAGE) {
      Serial.print("# Done! Elapsed: ");
      Serial.print((millis() - StartTime)/1000);
      Serial.println(" sec");

      SetIDrain(0.0);
      SetVGate(0.0);
      digitalWrite(PIN_DISABLE_IDRAIN,HIGH);
      digitalWrite(PIN_DISABLE_VGATE,HIGH);
      digitalWrite(PIN_ENABLE_HEAT,LOW);
      analogWrite(PIN_SET_IPELTIER,0);

      while (true) {
        TogglePin(PIN_HEARTBEAT);
        delay(25);
      }
    }

}

Everything is a compile-time option, which is certainly user-hostile. On the other paw, that allows me to get on with writing column instead of putzing around with the user interface… [grin]

About these ads

5 Comments