High voltage generation from 3.3-5V

This post describes my standard DC-DC HV converter

You know, to power up geiger tubes or photomultipliers there the need of high voltage at very low current drain. How to generate 400, 1000 or more volts from a single 5V supply or better 3,3V? Simple, let’s build a boost converter (Wikipedia). In this post I’ll describe my standard building block, the PWM drived, voltage multiplied boost converter. I’ve used it in many circuits, as notable example please take a look at my HV converter project page.

How does it works? Let’s look at the following picture. In my implementation the active element of the circuit, a N-type mosfet called BSP300, is capable to handle 800V and it only needs a proper PWM waveform generated by an MCU like Atmega328 from an Arduino or STM32F103 or other MCU’s to work.

My boost converter circuit

Briefly speaking, the input supply (5V) “charges” the magnetic field around the inductor L1 when the mosfet Q4 is energized by a positive PWM waveform (5V). The PWM signal is applied on the BSP300 gate. When the PWM signal reach the low state (0V) of it’s duty cycle, the mosfet disrupt the circuit causing the generation of an overvoltage across the inductor leads. Please visit Wikipedia for more info on PWM.

Duty cycle example

This overvoltage is caused by the magnetic field collapsing aroun the inductor. The overvoltage is rectified by a Shottky diode or, in my case by a voltage multiplier network. This voltage multiplier network is formed by D1/D2/D3/D4 and C2/C3/C4 and multiply input voltage by a factor of 4. The voltage multiplier network is a standard Cockcroft-Walton design.

Voltage multiplier basic circuit

At the output of the diode or the network usually you’ll add a capacitor to filter out the voltage ripple and then you have your high voltage ready to be used. The V_ADC output goes to an ADC input of the choosed MCU and makes possible to the MCU to know the output voltage then regulate it varying the PWM waveform duty cycle. The series of R1-R2-R3-R4 forms a 40Mohm resistor. With R5 that is 100kohm, this series of resistors is a voltage divider network. The voltage across R5 is 400 times smaller than the voltage across the complete resistor series. Mathematically Vr5=[Vin/(R1+R2+R3+R4)]*R5 so with 1600Vin you’ll get aprox. 4V. If your MCU runs from 5V and your ADC’s reference voltage is 5V you’ll stay in it’s input range. If you’ll use different reference voltage you’ll need to change the voltage divider ratio.

I’ve found this boost converter components calculator from Adafruit but I’ve found more usefull to use a standard MC34053 calculator and use it’s output inductor value as reference.

Adafruit calculator

How to generate the PWM signal? Take a look at this little “Arduino” code. It’s self explaining and it’s easy to be ported to whatever MCU and language you use.

Some notes about components selection

  • L1 must be a 3.3mH ferrite core inductor with the lowest internal resistance avaiable. It’s value is experimental, I’ve found that my circuit works best with this value. The inductor used is this ELC-11D332F
  • Diodes must be Shottky and rated minimum for 400V reverse voltage. Low leakage current fast recovery type are a must! Suggested model: BYG21M-E3/TR
  • Capacitors of the voltage multiplier must be rated minimum for 400V and they value is 4.7nF.
  • The BSP300 couldn’t be substituted. That mosfet is specifically designed to be drived by a PWM waveform from a MCU and it’s high voltage capability it’s essential. It’s also cheap and easy to found.

Some notes about my Arduino code

  • In this code I’m using A7 ADC pin to sample the generated voltage.
  • Pin 9 is PWM output to the BSP300 gate
  • A2-3-4-5 pins have internal pull up resistor enabled. This four pins are connected to a dip switch to select desired output voltage. The dip switch shorts to ground the selected line, the MCU read it as input.
  • The software calculate the desired output voltage via a simple function “read_jumper()”.
  • I’m also using a custom function called analogWrite16() because this function makes a 3Khz PWM output. The standard Arduino’s analogWrite() barely reach 900Hz frequency.
  • The frequency is very important because changing frequency you’ll need to change the value of the L1 inductor and also the capacitors of the voltage multiplier.
  • The circuit is designed to work at 3Khz.
//MadExp PMT adapter firmware v0.1
//Copyright 2018(C) Papadopol Lucian Ioan
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

//Define some pins
const int analogInPin = A7; // Analog input pin

//Voltage conversion constant
const float conversionFactor = 0.00488;

//Global variable
int Output;

//Startup setup code
void setup() {

//Jumper pullups
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A5, INPUT_PULLUP);

//Main LOOP
void loop() {

//Reading voltage set point and reading actual converter output voltage
int Setpoint = read_jumper();
int sensorValue = analogRead(analogInPin);
int Input = sensorValue * conversionFactor * 400;

//Some calculation for PWM correction (more voltage? less PWM exc...)
int k = 0 ; //Costante di correzione

if (Input < Setpoint) {
k = (Setpoint - Input) / Input;
Output = Output + 2 + k;
if (Input > Setpoint) {
k = (Input - Setpoint) / 10;
Output = Output - 2 - k;
analogWrite16(9, Output);

int setpoint_minus_delta = Setpoint - 20;
int setpoint_plus_delta = Setpoint + 20;
if (Input <= setpoint_plus_delta && Input >= setpoint_minus_delta) {
if (Input > setpoint_plus_delta || Input < setpoint_minus_delta) {


//PWM @ 16bit resolution! From 0 a 4095 not 0-255 like stupid arduino library!!!
/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
DDRB |= _BV(PB1) | _BV(PB2); /* set pins as outputs */
TCCR1A = _BV(COM1A1) | _BV(COM1B1) /* non-inverting PWM */
| _BV(WGM11); /* mode 14: fast PWM, TOP=ICR1 */
TCCR1B = _BV(WGM13) | _BV(WGM12)
| _BV(CS10); /* no prescaling */
ICR1 = 0x0fff; /* TOP counter value 4095 12bit */
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> 16000000/(65535+1)=244,14Hz
a 12 bit fa 16Mhz/(4095+1)= 3.9Khz
/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */

void analogWrite16(uint8_t pin, uint16_t val)
switch (pin) {
case 9: OCR1A = val; break;
case 10: OCR1B = val; break;

//Read jumper function
int read_jumper() {
int point;
int jumper_presel = 0;
int jumper_state1 = digitalRead(A2);
int jumper_state2 = digitalRead(A3);
int jumper_state3 = digitalRead(A4);
int jumper_state4 = digitalRead(A5);

if (jumper_state1 == 0) {
jumper_presel = jumper_presel + 8;
if (jumper_state2 == 0) {
jumper_presel = jumper_presel + 4;
if (jumper_state3 == 0) {
jumper_presel = jumper_presel + 2;
if (jumper_state4 == 0) {
jumper_presel = jumper_presel + 1;

point = (jumper_presel * 50) + 500;
return point;

Author: l.i.papadopol