Introduction: Make a Microcontroller-based Boost Converter

For a recent project, I needed to boost the output from a USB (high ampage charging) port from 5V up to 18V to power an amplified speaker. I decided to try rolling my own boost converter (mainly because playing with big inductors sounded cool). My first attempt was a dismal failure (it could only source about 10mA and I needed 600mA!) but, after reading TI's guide to calculating components, I managed to get it working pretty well. (I also converted the calculations in that guide into a spreadsheet to make it easier to work through it.)

It turns out that component selection is a reasonably big deal for a converter or this type. Even after you've got the right "headline" value for a component, there are other factors you need to worry about too...

Step 1: Basic Principle

Wikipedia has a good explanation of the principle but here's a quick guide:
  • The boost converter rapidly switches a switch on and off.  (My design runs at 65kHz.)
  • When the switch is closed (first diagram), it connects an inductor across the input supply while the diode blocks any current from flowing back from the output side.
  • The inductor charges up.  (Although it seems like shorting a coiled piece of wire across the input should waste a lot of power, the inductor actually stores up the energy in its core.)
  • When the switch opens, the inductor resists any change in current (and, shorting it across the supply means it has a lot of current going through it).  Since the output side has a much higher resistance than the switch, the inductor has to raise its voltage to keep the current flowing.  (Resisting change in current by changing their voltage is the magical property of inductors.)
  • The output capacitor charges up from the inductor plus the power supply at the higher voltage.
  • When the switch turns on again, the capacitor is charged at the higher voltage and powers the load until the next cycle.  Since power is only ever applied to the output side part of the time, there will always be a ripple on the output voltage.
  • If the switch is on for a relatively longer time in each cycle (it's duty cycle is higher) then the inductor stores up more energy, resulting in a higher output voltage when the switch turns off.  Controlling the duty cycle lets you adjust the voltage.
So much for the principle, how do you turn that into a real circuit?

Step 2: Choosing the Key Components

For exact values, I've put together a template Google spreadsheet that does the calculations from the TI guide. Below, I've tried to sum up the rules of thumb that I gleaned after reading that document and searching around.

Inductor
The inductor is the most important part of the circuit.

  • It's headline value is its inductance, measured in Henrys. The spreadsheet will help you calculate the right value. I recommend going for 1.5-2x the calculated value so you have some headroom.
  • You also need to check out:
    • The current rating, this needs to be enough to handle the peak current in the inductor (as calculated by the spreadsheet).
    • Shape: I went for a toroid because they're supposed to have low EMF interference. One source that I read said that bobbin inductors were the best bang for buck if you weren't worried about interference.
    • The core material, you want one that's suitable for a power inductor. I went for a toroid that was marketed as a power inductor. I believe it has a ferrite core.

I used this 150uH inductor.

Switch
This is the second most important piece of the circuit, and where I made a mistake first time around. A MOSFET is a good choice because it's easy to drive with a microcontroller. You need to look out for:

  • Rds(on) This is crucial, it's the resistance of the switch when it's turned on. My first attempt was scuppered by having a too-high value here. <10mOhm is ideal. If this is too high then the inductor won't be able to draw enough current and you'll waste power in the switch.
  • The Vgs(th) value, this is the voltage you have to apply to the gate of the transistor to turn it on. If you're using a 5V microcontroller, this needs to be 1-2V.
  • Vds(max), this is the maximum voltage the transistor can handle, go for the output voltage plus some safety margin.
  • Ids(max), the maximum current that the switch can handle. This needs to be bigger than the peak current according to the spreadsheet.

I used this switch.

Capacitors
The spreadsheet should calculate the minimum values for the capacitors in the circuit. I found that, powering an audio amp, I needed a much bigger output cap than was specified.

  • The capacitors in the output stage need to have a low ESR value for efficiency.

I chose a large, electrolytic capacitor with low ESR and then put in parallel with a 22uF ceramic capacitor in the hope of filtering the output further.

On the input side, I used the same setup.

Diode
The diode is fairly easy, just go for a Schottky diode that can handle the average current and has a low forward voltage (450mV seems to be the limit for non-exotic parts).

Microcontroller
I went for an ATTiny84A because it's available in through-hole packaging, it's not too big and the AVR GCC toolchain is pretty good. I followed this tutorial from Lady Ada to get the toolchain up and running and I used AVR Eclipse to develop the code. I needed fairly precise control of the hardware to get the PWM running at 65kHz so it might have been difficult with the more-abstract Arduino IDE.

Step 3:

Here's the circuit I came up with.

  • At its heart, the microcontroller (code to follow...) uses its PWM output to control the switch.
  • It monitors the output voltage using its ADC, via the feedback potentiometer.
    • If the voltage drops below the target, it increases the duty cycle of the PWM, increasing the current in the inductor and hence the output voltage.
    • If the voltage goes over the target, it decreases the duty- cycle.
    • the voltage can be adjusted by adjusting the pot.
  • The 440 ohm resistor on the output ensures that there's always some load on the converter. I found that value by trial and error. My input 5V power supply would shut down if I didn't draw enough load from it. You might be able to get away with a much larger resistance. It's essential to load the output though; with no load, the converter will become unstable and the inductor will put out a very large voltage.
  • The small capacitors in parallel with the large ones are filter capacitors. Large electrolytics have a relatively high resistance so putting a small ceramic or polymer cap in parallel helps to deal with transient spikes.
  • The 0.1uF cap on the ADC input is simply a filter.

Apologies for the poor-quality photos! I was so excited that it worked that I sealed it inside my project before taking a good shot.

Step 4: Microcontroller Code

I've put my working code up on Github.  It has a few parts:

These #defines and const declarations do compile-time calculations so that the code only needs to do simple uint8_t comparisons rather than floating point which is not feasible in a microcontroller.  Using const encourages the compiler to do the calculation at compile time and forces the type of the result to uint8_t.

#define PWM_FREQ 62500
#define PWM_RESOLUTION (F_CPU / PWM_FREQ)
#define MIN_DUTY_CYCLE 0.40
#define MAX_DUTY_CYCLE 0.80
const uint8_t MIN_PWM_LEVEL = PWM_RESOLUTION * MIN_DUTY_CYCLE;
const uint8_t MAX_PWM_LEVEL = PWM_RESOLUTION * MAX_DUTY_CYCLE;

#define VREF 1.1
#define DESIRED_VOUT 20.0
#define DIVIDER_RATIO 30.0
#define DESIRED_ADC_IN_V (DESIRED_VOUT / DIVIDER_RATIO)

const uint8_t DESIRED_ADC_RESULT = 255 * DESIRED_ADC_IN_V / VREF;

These define some useful utility macros so that the code is easier to follow:

#define DUTY_CYCLE_REG OCR0B

#define ADC_ENABLE() (ADCSRA |= _BV(ADEN))
#define ADC_START_CONVERSION() (ADCSRA |= _BV(ADSC))

The main function has an initial setup phase where it turns on the various peripherals that we'll need:

int main(void) {
    /* Set A7 as an output.  (Needed for PWM.) */
    DDRA |= _BV(DD7);
    PORTA = 0;

    /* Let input power stabilize... */
    _delay_ms(500);

    /*
     * Configure Timer0 as a fast PWM.  It will
     * - turn on the output pin at the start of each cycle
     * - turn it off when the value hits DUTY_CYCLE_REG
     * - wrap to 0 when it hits OCR0A
     */
    TCCR0A = _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);
    OCR0A = PWM_RESOLUTION;
    /* Start with 40% duty cycle and ramp up to avoid inrush. */
    DUTY_CYCLE_REG = (uint8_t)(PWM_RESOLUTION * 0.40);
    /* Set Timer0 clock source to be main oscillator. This enables the timer. */
    TCCR0B = _BV(CS00) | _BV(WGM02);

    /*
     * Turn on the ADC,
     * - use internal voltage ref.
     * - configure ADC0 as our source
     * - left-adjust the result, 8-bits is enough for us
     * - disable digital input buffer on pin
     * - enable the ADC.
     */
    ADMUX = /* REF = */ _BV(REFS1) | /* INPUT = */ 0;
    ADCSRA |= /* PRESCALER = 16 = 2^ */ 4;
    ADCSRB |= /* LEFT-ADJUST */ _BV(ADLAR);
    DDRA &= ~_BV(DD0);
    DIDR0 |= _BV(ADC0D);
    ADC_ENABLE();
    _delay_ms(1);

Then, it simply loops, reading the analog value from the potentiometer and comparing it to its target:

while (1) {
      /* Wait for the Timer0 to overflow... */
      loop_until_bit_is_set(TIFR0, TOV0);
      /* End of our OFF period, should be peak voltage... */
      TIFR0 |= _BV(TOV0);  /* Clear the flag. */

      /* Check the output voltage. */
      ADC_START_CONVERSION();
      loop_until_bit_is_clear(ADCSRA, ADSC);
      uint8_t adc_result = ADCH;

      if (adc_result < DESIRED_ADC_RESULT &&
          DUTY_CYCLE_REG < MAX_PWM_LEVEL) {
        DUTY_CYCLE_REG++;
      }
      else if (adc_result > DESIRED_ADC_RESULT &&
               DUTY_CYCLE_REG > MIN_PWM_LEVEL) {
        DUTY_CYCLE_REG--;
      }
    }
}

Step 5: Final Thoughts/next Steps

Caveat emptor: I'm not SMPS expert, and I've only built one.  I welcome any feedback from experienced engineers.

One weakness in my current design is that it has no inrush current protection.  I actually had to add a 0.2ohm resistor in series with Vin to avoid tripping the protection circuit in the USB charger.

I had a go at measuring the efficiency of my circuit by adding a shunt resistor to the input supply and loading the output with a high wattage resistor.  The absolute limit seems to be putting out about 25V, where the efficiency drops to about 50%.  At 18V, I get a respectable 75% and at 12V it's more like 80%+.