PFM BUCK

← all examples · interactive version of jupyter/buck_pfm.ipynb · voltage regulators
IDLE
UP (PMOS)
DOWN (NMOS)
V_o < VREF ?
CONTROL
One knob, not two: the PMOS is on for a fixed t_switch, and the only thing the loop decides is how often. That is the whole idea of pulse frequency modulation.
POWER STAGE
SIMULATION
Unlike the PWM buck this one needs no warm start: it begins just below VREF, as the notebook does, and the loop stops pushing the moment V_o passes VREF. The run has to be milliseconds long, though, or the averaging window holds too few pulses to mean anything.
V_o
RIPPLE
I_o
f_sw
CHARGE/PULSE TO LOAD
EFFICIENCY
Inductor current I_L
Load current I_o
Output voltage V_o
State machine
Measured, as you sweep the load

WHAT YOU ARE LOOKING AT

The same power stage as the PWM buck — same inductor, same capacitor, same two switches — under a completely different control law. There is no clock and no duty cycle. There is a three-state machine:

Every pulse delivers the same packet of charge, so the loop regulates by changing how often it fires. Draw twice the current and it fires twice as often. That is why the bottom right panel — switching frequency against load current — is close to a straight line through the origin, and it is the entire reason PFM exists: at light load a PWM converter still switches at full rate and burns the gate charge to do it, while a PFM converter simply goes quiet.

The price is ripple and spectrum. The output sawtooths between VREF and wherever the pulse pushes it, and the switching frequency moves around with the load, so the noise it makes is not at one predictable place. That is a bad trade in a radio and a good one in a battery-powered sensor that is asleep most of the time.

THINGS WORTH TRYING

THE PYTHON

state = 0  #0 = IDLE, 1 = UP, DWN = 2
for i in range(1,N):
    if(state == 0): # IDLE
        pmos = 0
        vx[i] = -100*ix[i-1] # model a high impedance
        if(vo[i-1] < VREF):
            state = 1
            t_start = ts
    elif(state == 1): # UP (PMOS on)
        vx[i] = VDDH - Rs*ix[i-1] - vo[i-1]
        pmos = 1
        if(ts - t_start > t_switch):
            state = 2
    elif(state == 2): # DOWN (NMOS on)
        pmos = 0
        vx[i] = 0 - Rs*ix[i-1] - vo[i-1]
        if(ix[i-1] < 0):
            state = 0

Source: jupyter/buck_pfm.ipynb. The state machine and the integration are the notebook's. Its own comment on the IDLE branch — "Want to model a high impedance. This works, but a bit nasty" — is worth reading: the 100 Ω term is a numerical stand-in for both switches being off, and it is why the inductor current decays instead of sitting at exactly zero between pulses.