0
\$\begingroup\$

I am trying to create a microcontroller (Arduino) based DC to DC buck converter with CC and CV features. For now, I am using a 20 V 5 A input source. Output voltage (target voltage) is settable by the potentiometer. Output current is fixed at 2.5 A in the Arduino code for the time being (but it will also be made adjustable in future). The ultimate goal to make a multipurpose battery charger with profiles for various battery chemistries and configurations, and with various safety features. But for now I am trying to get the buck part to work properly. I am using a 12 V 21 W bike lamp as test load. The inductor (L1) used in the circuit is salvaged from a desktop PC SMPS. I don’t know its value and I don’t have the equipment to measure it. It is working but I am facing the following two difficulties that I am unable to solve:

  1. If I connect capacitor C1 for smoothing the pulsing DC output, the capacitor as well as the MOSFETs get extremely hot within 30 seconds, so much overheated that if I let it run for some more time they will get fried. I tried various capacitor values from 100 uF to 4700 uF but the result is the same. Without the capacitor, it is able to drive the load without the MOSFETs overheating. They just get warm but I can touch them comfortably. It is pertinent to mention that I am not using any heat sinks for the MOSFETs as I am drawing less than 2.5 A total current. So why is the capacitor causing overheating of itself and MOSFETs and what is the solution?
  2. The Arduino is currently set to produce a PWM frequency of around 7.8 kHz. I tried various other frequencies like 10 kHz, 15 kHz, 31 kHz etc. but the MOSFETs overheat (even without the filter capacitor) at duty cycles < 60% (approx). However at 7.8 kHz or lower frequencies, the MOSFETs stay safe to touch across all duty cycle ranges.

I don’t have an oscilloscope so I can’t provide useful waveform data, but I am using the Raspberry Pi Pico based Scoppy.

NOTES: I am using a 1:10 resistor divider network (1 and 10 kΩ) for Scoppy probe and the readings are not compensated. So I have included the actual measured voltages in all the screen shots. Also, the PWM frequency and duty cycles are incorrect when the output filter capacitor is connected, so I have included the actual frequency and duty cycles in the respective screen shots.

Following screenshots are taken from the Scoppy app: enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

Arduino Code:

#include <Wire.h>

#include <LiquidCrystal_I2C.h>
#include <TimerOne.h>



LiquidCrystal_I2C lcd(0x27, 16, 2);

const int PWM_TIME_PERIOD_MICRO_SECONDS = 128; // Frequency =  1/time in seconds Hz
const int CURRENT_SENSOR_SENSITIVITY = 100;  
const int CURRENT_SENSOR_OFFSET_VOLTAGE = 2500;

const int VOLTAGE_SENSOR = A0;
const int CURRENT_SENSOR = A1;
const int VOLTAGE_CONTROLLER_POT = A2;
const int PWM_PIN = 9;
const int MAX_SETTABLE_VOLTAGE = 30;

const double TARGET_VOLTAGE_HARDCODED = 16.4;
const double PRECISION = 0.005; // + or - 0.5% error

const int VOLTAGE_SAMPLES = 1000;
const int BULK_DECREMENT_VALUE = 20;
const int BULK_INCREMENT_VALUE = 15;
const int BULK_INCREMENT_VOLTAGE_DEVIATION_PERCENT_THRESHOLD = 20; // Bulk increment when current voltage deviates from target voltage by 20% or more
const int BULK_DECREMENT_VOLTAGE_DEVIATION_PERCENT_THRESHOLD = 13; // Bulk decrement when current voltage deviates from target voltage by 13% or more
const int BULK_INCREMENT_CURRENT_DEVIATION_PERCENT_THRESHOLD = 20; // Bulk increment when current voltage deviates from target voltage by 20% or more
const int BULK_DECREMENT_CURRENT_DEVIATION_PERCENT_THRESHOLD = 13;
const bool VOLTAGE_SETTABLE_BY_KNOB = true;
const int MAX_DUTY_CYCLE_VALUE = 1023;
const double CURRENT_TOLERANCE_PERCENT = 5.0;

double TARGET_CURRENT = 2.5;  // 2.5 Amps
double TARGET_VOLTAGE;

int pwmDutyCycle = 0;

float outputVoltage;
float outputCurrent;

double acceptableLow;
double acceptableHigh;

// TODO:
// Ideally input voltage should be read from a sensor.
// So i need to implement that
int inputVoltage = 20;

void setup() {
  
  Serial.begin(115200);
  
  lcd.init();
  lcd.backlight();
  
  pinMode(PWM_PIN,OUTPUT);
  pinMode(VOLTAGE_SENSOR,INPUT);
  pinMode(CURRENT_SENSOR,INPUT);

  TARGET_VOLTAGE = TARGET_VOLTAGE_HARDCODED;
  Timer1.initialize(PWM_TIME_PERIOD_MICRO_SECONDS);
  Timer1.pwm(PWM_PIN, pwmDutyCycle);
}

 void loop() {
  if(VOLTAGE_SETTABLE_BY_KNOB == true) {
    TARGET_VOLTAGE = readVoltageControlKnob();
  }
  outputVoltage = readOutputVoltage();
  outputCurrent = readOutputCurrent();
  printOutputVoltageAndCurrent(outputVoltage, outputCurrent);
  adjustDutyCycle(outputVoltage, outputCurrent);
}


double readOutputVoltage(){
  double rawReading; 
  double sumOfReadings = 0;
  double average;
  double outputVoltageDetected = 0;
  
  int counter = 0;
  while(counter < VOLTAGE_SAMPLES) {
    rawReading = analogRead(VOLTAGE_SENSOR);
    sumOfReadings += rawReading;
    counter++;
  }

  average = sumOfReadings / VOLTAGE_SAMPLES;
  outputVoltageDetected = ((average / 1024.0 ) * 25);
  
  return outputVoltageDetected;
}


double readOutputCurrent() {
  int counter = 0;
  double rawReading = 0;
  double sumOfReadings = 0;
  double average = 0;
  double detectedAnalogVoltage = 0;
  double outputCurrentDetected = 0;
  
  while(counter < VOLTAGE_SAMPLES) {
    rawReading = analogRead(CURRENT_SENSOR);
    sumOfReadings += rawReading;
    counter++;
  }

  average = sumOfReadings / VOLTAGE_SAMPLES;
  detectedAnalogVoltage = ((average / 1024.0 ) * 5000);
  outputCurrentDetected = ((detectedAnalogVoltage - CURRENT_SENSOR_OFFSET_VOLTAGE) / CURRENT_SENSOR_SENSITIVITY);
  
  return outputCurrentDetected;
}


double readVoltageControlKnob(){
  double rawReading; 
  double sumOfReadings = 0;
  double average;
  double targetSetByKnob = 0;
  
  int counter = 0;
  while(counter < VOLTAGE_SAMPLES) {
    rawReading = analogRead(VOLTAGE_CONTROLLER_POT);
    sumOfReadings += rawReading;
    counter++;
  }

  average = sumOfReadings / VOLTAGE_SAMPLES;
  targetSetByKnob = ((average / 1024.0 ) * MAX_SETTABLE_VOLTAGE);
  
  return targetSetByKnob;
}

void adjustDutyCycle(double volts, double amps) {
  calculateAcceptableVoltageLimits();
  
    if(volts < acceptableLow && amps < tolerableCurrentLowerLimit()) {
      while(volts < acceptableLow && amps < tolerableCurrentLowerLimit()) {
        if(needsBulkIncrement(volts, amps)) {
          bulkIncrementDutyCycle();
        } else {
          incrementDutyCycle();
        }
        volts = readOutputVoltage();
        amps = readOutputCurrent();
        printOutputVoltageAndCurrent(volts, amps); 
        
        adjustTargetVoltageAccordingToKnob();
        Serial.println(volts);
      }
    }  if(volts > acceptableHigh || amps > tolerableCurrentUpperLimit()) {
      while(volts > acceptableHigh || amps > tolerableCurrentUpperLimit()) {
        if(needsBulkDecrement(volts, amps)) {
          bulkDecrementDutyCycle();  
        } else {
          decrementDutyCycle();
        }
        
        volts = readOutputVoltage();
        amps = readOutputCurrent();
        printOutputVoltageAndCurrent(volts, amps);
        
        adjustTargetVoltageAccordingToKnob();
        Serial.println(volts);
      }
    }
  
}

void calculateAcceptableVoltageLimits() {
   acceptableLow = TARGET_VOLTAGE - (TARGET_VOLTAGE * PRECISION);
   acceptableHigh = TARGET_VOLTAGE + (TARGET_VOLTAGE * PRECISION);
}

void adjustTargetVoltageAccordingToKnob(){
  if(VOLTAGE_SETTABLE_BY_KNOB == true) {
    TARGET_VOLTAGE = readVoltageControlKnob();
  }
  calculateAcceptableVoltageLimits();
}


void incrementDutyCycle() {
  if(pwmDutyCycle < MAX_DUTY_CYCLE_VALUE) {
    pwmDutyCycle++;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  }
}



void bulkIncrementDutyCycle() {
  if(pwmDutyCycle < (MAX_DUTY_CYCLE_VALUE - (3 * BULK_INCREMENT_VALUE))) {
    pwmDutyCycle = pwmDutyCycle + BULK_INCREMENT_VALUE;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  } else if(pwmDutyCycle < MAX_DUTY_CYCLE_VALUE) {
    pwmDutyCycle++;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  }
}


void decrementDutyCycle() {
  if(pwmDutyCycle > 0) {
    pwmDutyCycle--;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  }
}

void bulkDecrementDutyCycle() {
  if(pwmDutyCycle > BULK_DECREMENT_VALUE) {
    pwmDutyCycle = pwmDutyCycle - BULK_DECREMENT_VALUE;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  } else if(pwmDutyCycle > 0) {
    pwmDutyCycle--;
    Timer1.pwm(PWM_PIN, pwmDutyCycle);
  }
}


bool needsBulkIncrement(double volts, double amps) {
  double voltageDeviation = (float)((TARGET_VOLTAGE * 100)/volts) - 100.00;
  double currentDeviation = (float)((TARGET_CURRENT * 100)/amps) - 100;
  if(voltageDeviation > BULK_INCREMENT_VOLTAGE_DEVIATION_PERCENT_THRESHOLD && currentDeviation > BULK_INCREMENT_CURRENT_DEVIATION_PERCENT_THRESHOLD) {
    return true;
  }
  return false;
}

bool needsBulkDecrement(double volts,double amps) {
  double voltageDeviation = ((volts * 100)/TARGET_VOLTAGE) - 100.00;
  double currentDeviation = ((amps * 100)/TARGET_CURRENT) - 100.00;
  if(voltageDeviation > BULK_DECREMENT_VOLTAGE_DEVIATION_PERCENT_THRESHOLD || currentDeviation > BULK_DECREMENT_CURRENT_DEVIATION_PERCENT_THRESHOLD) {
    return true;
  }
  return false;
}


void printOutputVoltageAndCurrent(double currentVoltage, double amps) {
  String bufr;
  // Clear LCD Screen
  lcd.setCursor(0,0);
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print("                ");

  bufr = String("TV:");
  bufr.concat(TARGET_VOLTAGE);
  bufr.concat(" ");
  bufr.concat("PWM:");
  bufr.concat(pwmDutyCycle);

  lcd.setCursor(0,0);
  lcd.print(bufr);

  bufr = String("V:");
  bufr.concat(currentVoltage);
  bufr.concat(" ");
  bufr.concat("A:");
  bufr.concat(amps);
  
  lcd.setCursor(0,1);
  lcd.print(bufr);
}

double tolerableCurrentUpperLimit() {
  double tCurrent = (float) TARGET_CURRENT + ((TARGET_CURRENT * CURRENT_TOLERANCE_PERCENT)/100);
  return tCurrent;
}

double tolerableCurrentLowerLimit() {
  double tCurrent = (float) TARGET_CURRENT - ((TARGET_CURRENT * CURRENT_TOLERANCE_PERCENT)/100);
  return tCurrent;
}
````
\$\endgroup\$
9
  • 2
    \$\begingroup\$ I suggest you re-draw your schematic to fit more like a traditional switch mode "shape" and you will find that you have your inductor/diodes/mosfets incorrectly connected. \$\endgroup\$
    – Aaron
    Commented Jul 1 at 18:19
  • 1
    \$\begingroup\$ Think about this, when the FETs are on it causes current to flow in the inductor. But when the FETs turn off, the inductor current wants to continue to flow, so where does it go? \$\endgroup\$
    – Aaron
    Commented Jul 1 at 18:20
  • \$\begingroup\$ OK, so which capacitor is it, which exact model you bought? How much ripple you have in the output voltage, or ripple current in the capacitor? Do you know the ESR of the capacitor, or anything else about it for that matter? Just because it is a capacitor does not mean it will work well or at all how you need it to work in this circuit. \$\endgroup\$
    – Justme
    Commented Jul 1 at 18:25
  • \$\begingroup\$ @Justme The 4700uF is a 25V (HEXUN brand) and the 100uF is a 50V (KYS brand), both electrolytic capacitors. I don't know the ESR or any other data about these capacitors. \$\endgroup\$
    – irshukhan
    Commented Jul 1 at 18:39
  • \$\begingroup\$ @Aaron Can you please elaborate a bit. When the FETs are off where should the inductor current go? \$\endgroup\$
    – irshukhan
    Commented Jul 1 at 18:41

1 Answer 1

0
\$\begingroup\$

@Aaron was right. The MOSFET connections were messed up which didn't allow the inductor current to flow during off state of MOSFETs. The MOSFETs broke the current loop of the inductor while being in off state which caused the overheating of both the MOSFETs as well as the filter capacitor. I made the necessary modifications and the circuit is now working fine without overheating, even at higher currents than before. I am including the corrected schematic in case anyone might need it.enter image description here

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.