GTI v4 Writeup
GTI v4 Writeup
GTI v4 Writeup
0
James Fotherby, January 2023
Abstract
I wish I could present to you a working 250 W grid connected inverter. Alas, one of my
MOSFETs violently exploded during some tests and took down some other components with
it. I wanted to give up on my whole ambition to develop an open source 1kw hybrid inverter.
However after a few days my enthusiasm has rekindled. This can be done, I will do it, but
perhaps in a subsequent version. Since I wrote this report as I went along, I have published
this failed attempt regardless. I have definitely made ground since version 3 so I hope it has
some useful information in it.
Table of contents:
Abstract
Table of contents:
Introduction
PCB Design
PCB Stackup
EMI Considerations
PCB Assembly
Software Development
1. Upload a Blink Sketch
2. Setup timer 1 to output a fixed duty PWM
3. Modulate the PWM duty with 50 Hz LO
4. Set up the DFSDM peripherals
5. Build an overload protection system
6. Test thermal performance and efficiency
7. Build our PR controller
8. Build our PLL
9. Putting it all together!
10. Celebrate!
Introduction
Grid connected inverters are fascinating circuits and I have long dreamt of building a well
documented open source implementation. They are not trivial circuits to build because they
contain high voltages, fast switching transients and safety critical software. This is my 4th
attempt…
I had not found a single nuts & bolts approach to making such an inverter until I stumbled
across a great one on Instructables by a chap called Poldo. He writes well and has great
videos explaining his 100W current source inverter. He employs a fairly different approach to
the one presented here. His writeup is well worth a read.
Here are my previous published attempts GTI_v1, GTI_v2, GTI_v3 on instructables. It's
somewhat embarrassing looking back at these because they contain countless design flaws
and imperfections! These are some of the problems from GTI_v3:
The controller is vital for proper functionality and needs decent measurements of bus
voltage, grid voltage and grid current to work. We also measure filter capacitor current
because this allows us the potential to improve performance later on.
In previous versions I have built the 2 totem poles using MOSFETs and respective driver
chips. In this version I’ve used a pair of MASTERGAN2s. These packages house a driver
and a pair of FETs in a convenient SMD package. They are gallium nitride FETs which are
pretty trendy. (In retrospect, perhaps it’d have been better to keep things simple and use off
the shelf MOSFETs instead!)
An STM32L475 microcontroller is mounted on the PCB and galvanically isolated from the
high voltage section of the board. We measure the 4 key analogue signals: the DC bus and
grid voltages together with the grid and filter currents using AMC1306s. The STM32L475
has peripherals for receiving and processing the 4 separate sigma-delta data streams from
each of the AMC1306s. This allows simultaneous isolated sampling of all important metrics.
An Si8640 quad-channel digital isolator interfaces between the STM32L475 and the
MASTERGAN2s.
The AMC1306 chips need isolated power supplies which are generated by mini transformers
driven by MAX253 chips.
We wound the toroidal inductors to make the LCL filter. The plan is to do all high frequency
switching on the top MASTERGAN2 and switch the other leg at 100 Hz to unfold the
waveform. The idea being to contain all the switching noise and EMC to 1 principle area of
the PCB.
The LCL filter has an inverter side inductor Li and a grid side inductor Lg. We have split the
grid side inductor into 2 half sized inductors. (It was a gut feeling… this is probably
unnecessary).
A robust and stable film capacitor is used for the LCL filter. In GTI_v3 X7R MLCC capacitors
were used which have awful stability with changing voltage. Those capacitors were also
referenced to ground which is not right for grid connected inverters.
Relays have been included to connect or disconnect from the grid. A common mode choke
and some EMI filter capacitors have been added at the output. Ideally the whole PCB would
be shielded within a metal enclosure too.
By following the suggested designs for each of the various chips there was very little circuit
design left to do. It is very likely you could remove some capacitors and resistors but I was
quite conservative as I wanted to guarantee a working design. You might note that there is
no bulk capacitance other than the MLCCs and film capacitor (total ~11uF). That's because
it's all off board to save on PCB area and hence cost. Here’s the BOM with links to some of
the parts.
With grid connected applications the size of the filter capacitance is much smaller than for
stand alone sine wave inverters. I understand this to be to keep the reactive current <5% of
the nominal output current of the inverter.
I have made a GitHub folder of good papers within the subject of GTIs, including some on
LCL filter design. Simulations help to confirm resonant frequencies and attenuations etc. I
still think my filter could be better. Why are my inductors so much larger than the inductors in
commercial inverters!?
PCB Design
All the KiCad design files are on GitHub. The PCB design took a lot of work. In high
frequency circuits the PCB layout is as important as the schematic itself. At first glance you’d
be excused for thinking this is a low frequency circuit operating with modest PWM carrier
frequencies of ~40kHz. However we'll be hard switching 400 V signals with nanosecond
edge rates. Doing this requires frequencies hundreds of times higher than the fundamental.
I’m sure we’ll have content into the 10-100MHz range. A perfect square wave is composed
of a fourier series like so:
So a 400 V square wave at 40 kHz will have high frequency content eg. 40 V at 400kHz, 4 V
at 4 MHz and 400 mV at 40 MHz. We don’t want those signals radiating off our PCB or
contaminating our control signals.
In GTI_v2 there was such a careless approach to EMI that the 3.3V communication signals
between the microcontroller and AM1306s became so corrupted that it didn’t work beyond a
hundred volts! Good EMI design reflects good engineering…
PCB Stackup
I use PCBWay to manufacture my boards. They produce good quality PCBs for competitive
prices with excellent turnaround times (1-2 weeks). I'd recommend them.
The PCB is a 1.6mm 4 layer board. The stackup is such that the top and bottom 2 copper
layers are spaced 0.11mm apart. It took me a while to appreciate these tight spacings. I only
discovered it after sawing in half and inspecting a cross section of a spare GTI_v3 PCB and
then looking on PCBWays website to confirm.
These tight spacings are great for EMI though. Signals can run right on top of their
respective ground plane. I’m not quite sure whether having 400V between the 2 planes
would be acceptable. On searching the internet I concluded the FR4 material would not
breakdown at these electric field strengths but I should look into this further.
The MASTERGAN2 are laid out the same as in the TI reference design for their evaluation
board. (The pdf of this is in this folder on GitHub)
I positioned 0.22uF 450V MLCCs directly under each MASTERGAN2. Copper pour planes
will provide some further capacitance for the DC Bus. There’s a 10uF DC link film capacitor
mounted on the back of the PCB. A 470uF electrolytic capacitor rated to 400V is connected
inline with the wires feeding the DC bus input but is’t not mounted on the PCB to save
space. Altogether I think this provides adequate DC bus capacitance (~500uF).
To run a simulation we iterate over small timesteps and calculate the next current value
based on all the current variables:
𝑑𝐼
𝑉 = 𝐼 𝑅 + 𝐿 𝑑𝑡
𝐼𝑛+1−𝐼𝑛
𝑉 = 𝐼𝑛 𝑅 + 𝐿 ∆𝑡
We create 2 python files. One called model.py containing the following code:
class model:
def __init__(self, Ts):
self.Ts = Ts
#Model
I_m_next = V*Ts/L + I_m*(1-(R*Ts/L))
return(I_m_next)
# Simulation
for k in range(N):
# Set our output voltage
V = 1.0
# Plotting
plt.plot(t, data_I_m, "-b", label="Measured Current")
plt.plot(t, data_V, "-r", label="Driving Voltage")
plt.legend(loc="upper left")
plt.xlabel("Seconds (s)")
plt.ylabel("Amps or Volts")
plt.grid()
plt.show()
When this code runs it generates the following plot. This is exactly what you’d expect for
such an RL network with R = 0.5Ω and L = 2mH.
class controller:
def __init__(self, kp, ki, Ts):
self.kp = kp
self.ki = ki
self.Ts = Ts
self.E_Sum = 0
And we make some additions to our previous Simulation.py file so that it contains this:
# Controller Init
I_s=0.0
kp=1.0
ki=250.0
controller=controller(kp, ki, Ts)
# Simulation
for k in range(N):
# Set our desired output current
I_s = 1.0
# Plotting
plt.plot(t, data_I_m, "-b", label="Measured Current")
plt.plot(t, data_V, "-r", label="Driving Voltage")
plt.plot(t, data_I_s, "-y", label="Setpoint Current")
plt.legend(loc="lower right")
plt.xlabel("Seconds (s)")
plt.ylabel("Amps or Volts")
plt.grid()
plt.show()
The following plot is obtained. The yellow line is the setpoint current, we set this at the start
and leave it at 1 A. The controller adjusts the driving voltage V (Red) to achieve this setpoint.
You can see the measured current I (Blue) reach the setpoint and stay there. The controller
is tuned by adjusting 2 parameters Kp and Ki, in this plot they were set to 1 and 250
respectively.
By adjusting the tuning parameters you can reach the setpoint faster. With some
combinations you get an overshoot in the measured current such as in this plot with Kp = 1.5
and Ki = 600
Using a controller is great because it abstracts you from the system you are interfacing with.
You simply tell it what you want (a desired current) and it adjusts everything to achieve this.
PI controllers are great for DC type circuits but when the setpoint continuously varies the
controller is always playing catch up and you end up always lagging behind (i.e a phase
shift).
You can increase the tuning parameters but this comes at the cost of amplifying
measurement noise. Let’s adjust our code so that the current setpoint varies sinusoidally at
50 Hz.
with:
# Set our desired output current
I_s = np.sin(2*np.pi*50.0*T)
Additionally let’s add some gaussian distributed (white) noise into our measured current.
Replace:
# Iterate our simulated model
I_m = model.BasicModel(I_m, V)
With
# Iterate our simulated model
I_m = model.BasicModel(I_m, V) + np.random.normal(0.0, 0.0006)
And the tracking error only gets worse with higher frequencies of the setpoint signal. Let’s
implement a PR controller next. These are meant to be better because they allow you to
achieve zero phase shift error around their resonant frequency. They still use a proportional
term so you still get measurement noise being amplified into the driving voltage. They also
need tuning right to get zero phase shift.
4. Simulating an RL network driven by a PR controller with a
sinusoidal setpoint
Our model.py stays the same but we need to convert our PI controller to a PR controller. We
replace the controller.py code with
class controller:
def __init__(self, kp, kr, wres, wdamp, Ts):
self.kp = kp
self.Ts = Ts
self.kt = 2/Ts
self.E_Prev1=0
self.E_Prev2=0
self.U_Prev1=0
self.U_Prev2=0
self.E_Prev2 = self.E_Prev1
self.E_Prev1 = E
self.U_Prev2 = self.U_Prev1
self.U_Prev1 = Ui
V = (self.kp) * E + Ui
return V
To:
# Controller Init
I_s=0.0
kp=1.5
kr=10000.0
Wres_50 = 2.0 * np.pi * 50.0
Wdamp_50 = 2.0 * np.pi * 0.01
controller=controller(kp, kr, Wres_50, Wdamp_50, Ts)
With these tuning parameters we get a zero steady state phase error as shown in this plot:
The tuning parameters are too weak here because the amplitude is quite reduced from the
setpoint. By adjusting Kp = 8.0 and Kr = 100,000 we get far better tracking. You could easily
filter the high frequency measurement noise if desired.
The grid voltage should be a 50 Hz sine wave however the grid voltage is quite dirty in
reality. It contains harmonics at 150, 250, 350, 450 Hz and beyond together with spikes and
surges etc.
#Model
I_m_next = (V-Vgrid)*Ts/L + I_m*(1-(R*Ts/L))
return(I_m_next)
And here is the Simulation.py code with a few small changes and tuning adjustments
incorporating a 50Hz fundamental grid voltage in addition to a 150 Hz harmonic:
# Controller Init
I_s=0.0
kp=2.0
kr=10000.0
Wres_50 = 2.0 * np.pi * 50.0
Wdamp_50 = 2.0 * np.pi * 0.001
controller=controller(kp, kr, Wres_50, Wdamp_50, Ts)
###########################################################################
###########
# Simulation
for k in range(N):
# Set the Grid voltage
Vgrid = 1 * np.sin(2 * np.pi * 50.0 * T) + \
0.15 * np.sin(2 * np.pi * 150.0 * T + 0.0)
# Plotting
plt.plot(t, data_I_m, "-b", label="Measured Current")
plt.plot(t, data_V, "-r", label="Driving Voltage")
plt.plot(t, data_I_s, "-y", label="Setpoint Current")
plt.plot(t, data_Vgrid, "-g", label="Grid Voltage")
plt.legend(loc="lower right")
plt.xlabel("Seconds (s)")
plt.ylabel("Amps or Volts")
plt.grid()
plt.show()
Running all this generates the following plot. You can see the green trace is our grid voltage
containing some 150Hz harmonic content. Because our PR controller has a resonant
frequency at 50Hz the resonant term does nothing to cancel this frequency. As such you see
the measured current (blue) is distorted because it has 150 Hz content too.
This is bad. We requested a 50 Hz clean sinusoidal output current but are getting distortions
and harmonic content leaking through. An inverter using this may fail regulatory approval on
grounds of harmonic distortion.
To improve this issue we can create another instance of our PR controller and run the two in
parallel. The second PR controller can have a resonant frequency centred on the 150 Hz
harmonic:
We call our PR controller instances controller50 and controller150. The former is the only
one with any Kp gain:
# Controller Init
I_s=0.0
kp=2.0
kr=10000.0
Wres_50 = 2.0 * np.pi * 50.0
Wdamp_50 = 2.0 * np.pi * 0.001
controller50=controller(kp, kr, Wres_50, Wdamp_50, Ts)
kr=100000.0
Wres_150 = 2.0 * np.pi * 150.0
Wdamp_150 = 2.0 * np.pi * 0.001
controller150=controller(0, kr, Wres_150, Wdamp_150, Ts)
We also add the controller150 into our main simulation for loop
# Iterate our controller
V = controller50.Iterate(I_m, I_s) + controller150.Iterate(I_m, I_s)
This results in the following plot which is arguably quite a bit better. The above method is
called harmonic compensation. Several compensators can be used, each working at the
major harmonics.
I didn’t optimise the tuning parameters in these simulations. I just wanted to get the point
across. Here’s a paper called Tuning Rules for Proportional Resonant Controllers. With the
right tunings you can get pretty good tracking.
PR controllers have a given bandwidth which means they take time to respond and in some
of the plots above you can see the measured current waveform evolving cycle to cycle
following start-up. I think this is more pronounced with poor tuning settings.
We need a LO because it’ll be used as the setpoint reference for our output current
controller (independent of whether that controller is a PI, PR, D/Q, dead-beat, MPO). We
must output a current waveform that is in phase and frequency (i.e. grid tied) with the grid.
Therefore we need our LO adjusted continuously so it has exactly the same phase (and
therefore frequency) to the grid waveform (i.e phase locked). The way we make these
adjustments is via a PLL.
The grid signal provides our reference waveform and will include noise and harmonic
content. When the program starts our LO will initially be a pure 50 Hz cosine with an
arbitrary phase. It'll need some prompt adjustment!
Let’s talk through the maths of our phase detector. The job of which is to measure the phase
difference between the 50 Hz grid fundamental and LO signal. We start by simply multiplying
the 2 signals:
1 1
𝑉𝐺 × 𝑉𝐿𝑂 = 𝑠𝑖𝑛(ω𝐺𝑡)𝑐𝑜𝑠(ω𝐿𝑡 + ϕ) = 2
𝑠𝑖𝑛((ω𝐺 + ω𝐿)𝑡 + ϕ) + 2
𝑠𝑖𝑛((ω𝐺 − ω𝐿)𝑡 − ϕ)
Looking at this graphically, 𝑉𝐺 in green is our 50 Hz grid signal. 𝑉𝐿𝑂 in red is our 50 Hz LO
with a 30 degree phase error. The resulting multiple is in blue showing a 100 Hz signal riding
on a DC bias.
Before we talk about the yellow line we realise that in normal operation: ω𝐺 = ω𝐿 which
simplifies the maths to:
1 1
𝑉𝐺 × 𝑉𝐿𝑂 = 2
𝑠𝑖𝑛(2ω𝐺𝑡 + ϕ) − 2
𝑠𝑖𝑛(ϕ)
Next we maintain a running integral of this multiplication over the last period (20ms for a 50
Hz signal). In doing this we obtain the yellow line. Note at startup the running integral is not
yet “filled” so there’s some brief disturbance. What’s nice about this is that integrals of
periodic functions over a period are equal to zero so:
2π
𝑡+ ω
𝐺
1 −1 −1
∫ 2
𝑠𝑖𝑛(2ω𝐺𝑡 + ϕ) + 2
𝑠𝑖𝑛(ϕ)𝑑𝑡 = 2
𝑠𝑖𝑛(ϕ)
𝑡
This is great. It means that the result of our running integral is a measure of the phase
difference between the grid and our local oscillator. We can use this as the error signal for a
PI controller. If the phase difference is ever non zero the frequency of our LO is adjusted to
return the difference to zero.
Samples_per_Period = 400
Periods_to_plot = 4
Samples=Samples_per_Period*Periods_to_plot
Time_of_simulation = 0.02*Periods_to_plot
# Plotting
plt.plot(t, LO, "-r", label="LO")
plt.plot(t, Vgrid, "-g", label="Grid V")
plt.plot(t, Signal_Multiple, "-b", label="Signal_Multiple")
plt.plot(t, Integral, "-y", label="Running Integral")
plt.legend(loc="lower right")
plt.xlabel("Seconds (s)")
plt.ylabel("Arbitrary")
plt.grid()
plt.show()
Say for example at system startup there was a perfect 180 degree phase error between our
grid and LO signals. Our phase detector would output a zero error so we wouldn’t initially
adjust our LO frequency. However we are at an unstable equilibrium point and any slight
deviation in phase will result in positive feedback. We’ll then either decrease our LO
frequency to bring the phase error to 0 degrees. Or alternatively depending on which way
the cookie crumbles we’ll increase our LO frequency until the phase error is +360 degrees
(which is equivalent!).
3) If we don’t integrate over exactly a period. Say we always integrate over 20 ms but
our grid and LO are both running at 50.2 Hz - then we’ll get a very small amount of
signal_multiple leaking through into our error signal. The following plot is with our grid
and LO at 49 Hz (The grid would never deviate this far). You can see some 98 Hz
leaking through but it’s clearly nothing to worry about.
Enough with python simulations! They have been key to building up some intuition but we
need to start implementing all this in real time on actual hardware with real world signals.
PCB Assembly
The PCB boards have arrived. To assemble the PCB I applied solder paste using a stainless
steel stencil before mounting the components and reflowing in a cheap toaster oven. It took
a morning.
The parts reflowed beautifully. I used this Mesh 4 Sn63Pb37 solder paste
Once assembled I cautiously powdered up the logic section of the board. It pulled 80 mA.
The mini transformers were all working nominally and every section was getting the
appropriate supply voltage - Win!
Software Development
I chose to use the STM32L475 microcontroller because it had four digital filters for sigma
delta modulators. It was also in stock! I had a 10-point plan for the software development:
1) Make a new project, blink the onboard LED and toggle the relays
2) Set up timer 1 to output a fixed duty PWM to drive the MASTERGAN2s
3) Set up the DFSDM peripherals to read the AMC1306s and write values to main
memory via DMA
4) Modulate the PWM duty with a 50 Hz LO to create a voltage source inverter
5) Build our overload protection system
6) Test thermal performance and efficiency of the circuit driving a resistive load
7) Build our PR controller and test it driving a current through an RL load
8) Build our PLL and ensure it’s synchronising with the grid voltage
9) Put it all together! Push power into the grid
10) Celebrate!
1. Upload a Blink Sketch
Blinking an LED is a good milestone because it means you’ve set up your development
environment, connected to the target and uploaded some code. It confirms that the
microcontroller is powered properly by the PCB and is operating correctly.
12V was applied to the logic section and the programming header was connected to the
SWD port of a STM32F412 Dev board. We can use ST-Link to upload code.
STM32CubeIDE was used to write the software. It’s free and incorporates STM32CubeMX.
This latter piece of software provides a graphical interface to configure the clock settings and
all the peripherals. It's incredibly helpful and saves an enormous amount of effort. (On niche
stuff it can have the odd bug)
A 16 MHz crystal is mounted on the PCB and STM32CubeMX can calculate all the PLL
settings to give you the system clock speed you want. In this case we'll max out the clock
speed at 80 MHz. Here are the clock settings on CubeMX:
CubeMX makes it a piece of cake to choose various pins and register them with the
peripheral you want. We configure the LED Pin (PA2) and relay pin (PA11) as an output and
with a click of a button all the code is done for you.
This goes in the main loop between the /* USER CODE BEGIN 3 */ and /* USER CODE END
3 */ statements. The code uploaded and ran on the first attempt which was a huge relief!
A duty cycle between 0-1000 and PWM frequency of 40kHz means our pulse width
increments in steps of 25ns. The MASTERGAN2 datasheet suggests a minimum pulse width
of 120ns to avoid distorted output waveforms. We can tolerate a degree of distortion, I doubt
it'll be significant because we will rarely be operating with such small duties.
We have to add a few lines of code into our main.c file to start the timers on both the regular
and complementary outputs:
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
We can set the duty to 50% (500/1000) by directly writing to the CCR registers. We'll keep
the bottom GaN (Channel 1) low and only switch on the top GaN (Channel 2 - which directly
feeds into the inverter inductor):
htim1.Instance->CCR1 = 0;
htim1.Instance->CCR2 = 500;
The switching node voltage was measured on a Picoscope 4224A with a 15V bus voltage.
We can see beautiful sharp edges. The deadtime was reduced to 25ns based on these
waveforms. With longer deadtimes the dead-zone was clearly visible but at 25ns it had just
disappeared.
The inverter's output was connected to a 6 ohm power resistor and a few input and output
measurements were taken using a multimeter (quoted accuracies) with a 20% applied duty
cycle:
- DC Bus input voltage = 14.99 V (+/- 0.5%)
- DC Bus input current = 81.8 mA (+/- 1%)
- Inverter output voltage = 2.54 V (+/- 0.5%)
- Inverter output current = 420 mA (+/- 1%)
These measurements show an 87% efficiency. At 1st glance that seems rubbish but it was
entirely expected for these low operating voltages. The reason is that this inverter is
designed for DC bus voltages of 380 V.
Let’s measure the inverters output resistance. We set the duty to 0% so both GaNs are
switched to GND continuously. We can then apply a current through the output and measure
the voltage drop. This current must travel through all the inductors, PCB traces and through
both GaN low-side drain-source resistances. It is an excellent measurement of the inverter’s
DC output impedance.
This created a plot from which linear regression provides us with a value of 0.95 ohms.
It now makes complete sense why we only measured 87% efficiency. 0.95 ohms is large
compared to the 6 ohm load we were driving. Hence for a given current, 6/6.95 of the input
power will enter the load and 0.95/6.95 will be dissipated by the inverter’s internal
impedance. Note that 6/6.95 = 86% which is very close to our measured 87% efficiency. All
good so far.
A 10 MHz DFSDM clock is suitable to feed the AMC1306’s which can accept clocks between
5-20 MHz. The AMC1306 datasheet advises a Sinc3 filter. Here’s A fantastic resource
explaining sigma-delta modulation and the config settings. I set Fosr=125 and Iosr=4. The
data will have a dynamic range of +/- For^3 x Iosr = +/- 7812500. The data rate will be
10MHz/(Fosr*Iosr) = 20khz. This is an awesome amount of resolution. The values are
automatically written to main memory by the DMA. (Note. the data is present in the most
significant 24-bits of the 32-bit word)
I calibrated the readings by taking various measurements and plotting graphs of best fit
again. Each time the DMA completes a conversion the respective result is converted to a
floating point value in SI units. The IDE has a debug mode so it’s easy to read off the values
the microprocessor is acquiring.
When the LO value is wanted we'll simply put this index through a sine or cosine function to
get the respective sample. Here are is the code for our timer ISR:
We configure a second timer to run a periodic function at ~13 kHz. Each time this function
runs it gets the LO sine value and writes to our duty. This modulates our duty cycle with a 50
Hz sine wave. We improve our resolution beyond 64-steps by performing some interpolation.
if (htim == &htim3)
{
static int16_t Duty_Cycle;
if(Duty_Cycle >= 0) {
htim1.Instance->CCR1 = 0;
htim1.Instance->CCR2 = Duty_Cycle;
}
else {
htim1.Instance->CCR1 = 1000;
htim1.Instance->CCR2 = 1000 + Duty_Cycle;
}
}
This makes Channel 1 switch between V_Bus and GND every half cycle. All the high
frequency PWM is done on channel 2 which directly feeds the inverter inductor.
With the 6 ohm shunt resistor on the output we can now see a stunning 50 Hz signal across
it (in red using a 50x differential probe):
The DC bus was given 25 V and our PWM duty was modulated by a sine wave to swing to
+/-97%. The picoscope measured an RMS voltage of 14.53 V across the 6 ohm load which
implies an RMS current of 2.42 A. The GaNs were only warm to touch at about 25 degrees
celsius above ambient.
Also superimposed on this view is the AC component of the DC bus voltage. You can see
how this waveform is twice the frequency of the 50 Hz output signal. This is expected
because maximal power flows into the load twice per cycle and hence the DC bus voltage is
going to sag twice per period. There are a few other things to note here:
1) We can see distortion occurring at the zero crossing points of the output signal.
Adding DC bus bulk capacitance helped (We have ~500uF). I’m not sure where this
distortion is coming from and in hindsight I should have investigated further. We know
that at very low PWM duties the GaNs will distort because they need a minimum
input pulse duration but I don’t think it explains this finding. From the MASTERGAN2
datasheet you can see exactly what it says on the matter:
2) The above DC bus waveform was shown with an 8kHz digital filter applied. Without
any scope filtering there is a large amount of 40kHz switching noise riding on this 100
Hz signal. This could perhaps be improved with more DC bus capacitance.
Electrolytics would be poor at suppressing such high frequencies so we’d need more
MLCCs. I suppose this is why EMI filters are also present on the input to such
converters.
But let’s focus on our output waveform, it’s a stunner isn’t it!
5. Build an overload protection system
Conveniently the DFSDM peripheral has a built-in analogue watchdog feature. If configured
thresholds are crossed it triggers an interrupt which could disable the output. But I couldn’t
get this to work! So we’ll just have to set up another periodic function at 1kHz and check the
vitals there. (Perhaps this was my downfall…)
HB_Disable();
Grid_Good_Bad_Cnt = GRID_UNACCEPTABLE;
Error_Code |= 0b00001;
}
HB_Disable();
Grid_Good_Bad_Cnt = GRID_UNACCEPTABLE;
Error_Code |= 0b00010;
}
// These are lower priority checks and have to be out of range for time
if(Mains_RMS > RMS_UPPER_LIMIT || Mains_RMS < RMS_LOWER_LIMIT) {
Grid_Good_Bad_Cnt -= GRID_BAD_FAIL_RATE;
Error_Code |= 0b00100;
}
Error_Code |= 0b01000;
}
Grid_Good_Bad_Cnt -= GRID_BAD_FAIL_RATE;
Error_Code |= 0b10000;
}
HB_Disable();
Grid_Good_Bad_Cnt = GRID_UNACCEPTABLE;
}
if(HB_Enabled == false) {
E_Prev2 = E_Prev1;
E_Prev1 = I_OUT_PID.error;
U_Prev2 = U_Prev1;
U_Prev1 = Ui;
The value I_Output_Demand determines the peak output current setpoint. The LO has an
amplitude of 256 and so I_Output_Demand = 4e-3 then the setpoint peak current is about
1A.
I wanted a more real time method of seeing the output currents/voltages the inverter was
measuring. I did this by using the STM32L475’s DAC and attaching a scope to its output.
The metric to debug can then be written in real time to the DAC and the scope can see the
values being measured. This works really well. The measured values had excellent
resolution, were accurate and noise free.
The plot below shows the output waveform and the DAC outputting the LO’s phase_index
which increments in 64-steps.
I was noticing small glitches in the output voltage waveform. Despite spending hours working
out where these were coming from, I couldn’t find the source. Nevertheless the PI+R
controller was nicely adjusting the output voltage waveform to drive a setpoint current. With
a DC bus of 20V, I could short the output and the controller would immediately respond by
reducing the output voltage to continue to drive the setpoint current. Excellent!
I figured I was still in safe waters driving the flood lamp and using DC bus voltages of 350V. I
initially set quite a low I_Output_Demand. The current ramped up and lit the flood lamp dimly
using the PI+R controller to meet the setpoint current. The setpoint wasn’t being reached so
I had to set a higher setpoint than what I wanted delivered. With the lamp at half power of
200W the power supply was having to deliver about 15A to the MSW inverter
Eager to see the lamp flooding my shed with light, I increased I_Output_Demand. I saw my
power supply current build through 4A, 8A, 14A, 20A and 24A before the circuit cut out. I
was used to the grid checks cutting the inverter if something went funky. In the back of my
head I felt a little uneasy. I’d heard a faint unfamiliar noise. I can’t remember exactly what
occurred next. I believe I reset the circuit hoping the lamp would come on. A moment later a
deafening bang and a blue spark the size of my fist erupted from a MASTERGAN. I froze
and reflected on how dangerous the inverter had been all along. I switched everything off
and took note of what had just happened.
Thoughts
● I think the MASTERGAN had undergone shoot-through. The DC bus would have
discharged through the device and exploded it. Interestingly there was still voltage on
the DC bus so perhaps the MASTERGAN had failed open circuit.
● Both upper and lower GaN MOSFETs in the package had exploded (2 little craters). I
think this provides evidence it was secondary to shoot-through.
● Why had shoot through happened though? The MASTERGANs are supposed to
have logic inside preventing shoot-through and we had programmed a dead-time.
● Perhaps an overcurrent situation occurred. I had a 2A fuse to the flood lamp but
instead perhaps the grid side inductor saturated and a huge current flowed through
the filter.
● If a single GaN MOSFET failed closed circuit it’d only take the other FET in the
package to switch on to cause a shoot-through.
● Over-temperature - But the MASTERGANs have an inbuilt thermal cutout. They also
never got hot in any of my earlier tests.
● It’s interesting that the explosion happened in the lower MASTERGAN. This is the
package that only switches at 100Hz. Perhaps it hadn’t turned on properly. Or,
perhaps it began to stop conducting properly within a switching period causing some
sort of failure mode.
● Perhaps the 5V logic supply voltage to the MASTERGANs had gone out of range
causing a failure. I think this is unlikely because the voltage regulators were working
well within their specifications.
● Interestingly there are a number of failed capacitors. My best guess is that sparking
jumped 380V within the chip. This would back feed all 5V bypass capacitors with
380V destroying them. It’d also damage all chips supplied with this rail.
● Could a resonance have built in the filter network causing a breakdown? I think this is
an unlikely explanation.
● Could the PCB have broken down? I did have 380V between 2 copper planes
spaced 0.11mm apart. However the dielectric breakdown of FR4 is supposed to be
20kV/mm. That’s ample in this case. Perhaps some moisture was in the wrong place.
Nevertheless I wouldn’t expect this to cause the MASTERGAN to explode in this way.
● Perhaps the top MASTERGAN was operating at a high duty cycle (The limit used
was 994/1000) Perhaps it’s high-side bootstrap capacitor discharge mid cycle. I can’t
see how this would cause the bottom package to explode but it’s not out of the
question.
Given the failure occurred at high currents, I think the most likely explanation is this: The
grid-side inductor reached saturation causing an overcurrent situation. The bottom
MASTERGAN low-side FET latched on due to the excess currents. The lower MASTERGAN
then switched on its high-side FET and shoot-through blew the package apart.
Conclusion
I will try again! I don’t want to give up until we have accomplished an open source 1kW
hybrid inverter.
Not so well:
● Having standard TO-220 MOSFETs might make the system more robust and fixable
● I must get the DFSDM analog watchdog working
● Is the filter optimal? Why are my inductors so big compared with powerful
commercial inverters?
● Incorporate a UART interface for debugging/communications and breakout the DAC
pin
I hope that this writeup has been educational or interesting. I have thoroughly enjoyed
building version 4 and I have made good ground toward the final goal. Please leave
comments and ask questions. I love hearing all your opinions and suggestions.