The Other Kind of Bypass Capacitor
There’s a type of bypass capacitor I’d like to talk about today.
It’s not the usual power supply bypass capacitor, aka decoupling capacitor, which is used to provide local charge storage to an integrated circuit, so that the highfrequency supply currents to the IC can bypass (hence the name) all the series resistance and inductance from the power supply. This reduces the noise on a DC voltage supply. I’ve written about this aspect of Hbridge design before, and Intersil has a good application note on selecting bypass capacitors. When in doubt, use ground and power planes, and some 0.1μF and 0.01μF surface mount capacitors nearby each IC that needs to connect to the planes, with vias between capacitors and the planes located as close as posssible to the capacitor pads; the 0.1μF is a good general value capacitor for charge storage, and the 0.01μF capacitor, in a small package like 0603 or 0402, will have better highfrequency characteristics for those ICs that really need to have a quiet power supply, or which have lots of switching spikes — use them on ADCs and microcontrollers, for example. (If you want more of the gory details, read Clemson University’s articles on EMI design and PCB layout.)
You probably know all that already.
But I’m going to talk about another type of bypass capacitor. Here are three practical examples where it is used:

C1 in a portion of a switching regulator circuit, from ON Semiconductor’s Application Note AND8327/D — authored in part by the eminent Christoph Basso

The 1000pF cap across the LF411 in the “Son of Godzilla Booster” from National Semiconductor’s AN272, allegedly written by Jim Williams:

The 100pF cap across the LT1190 in a highpower wideband current source, from Linear Technology’s AN47, also by Jim Williams. (In fact there are several other circuits in this appnote that use the same technique.)
These are examples of circuits with feedback bypass capacitors. In all three cases, we have an error amplifier with gain \( K(s) \), feeding an ugly complex system with some inputtooutput gain \( U(s) \) (U for “ugly”) through some equivalent resistance \( R \), along with a capacitor \( C_b \) that bypasses the feedback.
In the AND8327 appnote (“Figure 1” including the TL431), this capacitance is C1 (100nF) and the resistance R is the Thévenin equivalent \( R2  R3 = 5\text{k}\Omega \); the “ugly” system includes the transconductance of the TL431, the current transfer ratio of the optoisolator, the pullup resistor, and the smallsignal equivalent of whatever switching power supply stage relates the input FB to the output Vout.
In the Son of Godzilla Booster, the feedback capacitance is the 1000 pF capacitor across the LF411 and the resistance is the 1M capacitor at the top of the circuit (with 300pF parallel capacitance to provide some highfrequency feedback); the “ugly” system is the LM3524 switching converter, transformer, and rectifier.
In the wideband current source, the feedback capacitance is the 100 pF capacitor across the LT1190, and the resistance is the 2K resistor from the LT1194, which serves to correct any nonlinearities or offsets of the LT1190; the “ugly” system is the LT1194 and the transistor current mirror circuit.
The idea is that \( U(s) \) includes DC and lowfrequency feedback that we care about, but for various reasons its highfrequency characteristics can’t be trusted. The capacitor gives us unitygain feedback at highfrequencies, where feedback from the ugly complex system is attenuated.
Our overall loop gain is \( K(s)F(s) \) where $$F(s) = \dfrac{\frac{U(s)}{R} + C_bs}{\frac{1}{R} + C_bs} = \dfrac{U(s) + RC_bs}{1 + RC_bs}$$ is the net feedback transfer function, derived via Millman’s Theorem. At low frequencies (\( \omega \ll 1/RC_b \)), \( F(s) \approx U(s) \), and at high frequencies \( F(s) \approx 1 + \dfrac{U(s)}{RC_bs} \approx 1. \)
An example
Let’s look at a particular example in more detail. Here let’s say that \( U(s) \) is a unitygain power amplifier (with some characteristic time constant \( \tau \)) followed by an LC circuit with some parasitic series resistance \( R_s \) in the inductance.
So we can model \( U(s) = \dfrac{1}{\tau s + 1}\cdot\dfrac{1/Cs}{Ls + R_s + 1/Cs} = \dfrac{1}{\tau s + 1}\cdot\dfrac{1}{LCs^2 + R_sCs + 1} \). The LC circuit we can treat as a 2ndorder system, so \( U(s) = \dfrac{1}{\tau s + 1}\cdot\dfrac{1}{s^2/\omega_n{}^2 + 2\zeta s/\omega_n + 1} \) where \( \omega_n = 1/\sqrt{LC} \) and \( \zeta = \frac{R_s}{2}\sqrt{\frac{C}{L}} \). For wellconstructed inductors \( \zeta \) will be small: take for example \( L = 100\mu \text{H} \), \( R_s = 0.2 \Omega \), and \( C = 47\mu \text{F} \); this gives us \( \omega \approx 14587 \) rad/s and \( \zeta \approx 0.145 \).
What about the opamp’s \( K(s) \)? Well, we can approximate \( K(s) = \dfrac{B}{s + B/K_{DC}} \) where \( B \) is the gainbandwidth product in rad/s, and \( K_{DC} \) is the DC gain. Let’s assume we have a 1MHz GBW opamp with a DC gain of 200,000.
If we choose \( R = 100K\Omega \) and \( C_b = 1\mu F \), we can draw Bode plots of the overall loop gains \( KF \) (with the capacitor \( C_b \)) and \( KU \) (without). Jump past the Python code unless you’re really interested; it’s just there to draw pictures.
import numpy as np import matplotlib.pyplot as plt def opamp_tf(B, Kdc): def K(s): return B/(s+B/Kdc) return K def ugly_tf(L,C,Rs,tau): def U(s): return 1/(tau*s + 1)/(L*C*s*s + Rs*C*s + 1) return U class LimitFinder(): def __init__(self): self._min = None self._max = None def __iadd__(self, data): newmin = np.min(data) newmax = np.max(data) if self._min is None: self._min = newmin self._max = newmax else: self._min = min(newmin, self._min) self._max = max(newmin, self._max) return self @property def min(self): return self._min @property def max(self): return self._max def ticks(self, delta, clipmin=None, clipmax=None): tmin = self.min if clipmin is None else max(clipmin,self.min) tmax = self.max if clipmax is None else min(clipmax,self.max) return np.arange(tmin//delta*delta,tmax+delta,delta) def __repr__(self): return "[%s,%s]" % (self.min, self.max) def interpolate_steep(x, ylist, max_interp=100): n = len(x) ninterp = [] for y in ylist: ymin = np.min(y) ymax = np.max(y) dyref = np.abs(ymaxymin)/n if dyref < 1e10: alpha = x*0 else: alpha = np.abs(np.diff(y))/dyref ninterp.append(alpha) ninterp = np.floor(np.max(np.array(ninterp),0)) ninterp = np.minimum(max_interp, ninterp) xinterp = [] for k in np.argwhere(ninterp > 1): x1 = x[k] x2 = x[k+1] dx = (x2x1)/ninterp[k] xinterp.append(x1+np.arange(1,ninterp[k])*dx) return np.hstack(xinterp) def bodeplot(Hfunclist, fig=None, npoints=100, maglimits=None, xlim=None): if xlim is None: xlim = [2,6] else: xlim = np.log10(xlim) omega1 = np.logspace(xlim[0],xlim[1],npoints) lxlim = [omega1[0],omega1[1]] db = lambda x: 20*np.log10(np.abs(x)) ang = lambda x: np.angle(x,deg=True) ticks = lambda xmin,xmax,delta: np.arange(xmin//delta*delta,xmax+delta,delta) if fig is None: fig = plt.figure(figsize=(8,6)) ax1 = fig.add_subplot(2,1,1) ax2 = fig.add_subplot(2,1,2) omega_additional = interpolate_steep(omega1, [f(Hfunc(1j*omega1)) for f in [db, ang] for Hfunc in Hfunclist], max_interp=200) omega = np.union1d(omega1,omega_additional) maglim = LimitFinder() phaselim = LimitFinder() for Hfunc in Hfunclist: H = Hfunc(1j*omega) ax1.semilogx(omega,db(H)) maglim += db(H) ax2.semilogx(omega,ang(H)) phaselim += ang(H) ax1.set_ylabel(r'$20\, \log_{10} H(\omega)$', fontsize=16) ylim = [maglim.min, maglim.max] if maglimits is None: maglimits = (None, None) if maglimits[0] is not None: ylim[0] = max(maglimits[0], ylim[0]) if maglimits[1] is not None: ylim[1] = min(maglimits[1], ylim[1]) ax1.set_yticks(maglim.ticks(10, clipmin=maglimits[0], clipmax=maglimits[1])) ax1.set_ylim(1.05*ylim[0]0.05*ylim[1],1.05*ylim[1]0.05*ylim[0]) ax1.grid('on') ax2.set_yticks(phaselim.ticks(15)) if phaselim.min < 175 and phaselim.max > 175: ax2.set_ylim(180,180) ax2.set_ylabel(r'$\measuredangle H(\omega)$',fontsize=16) ax2.set_xlabel(r'$\omega$ (rad/s)',fontsize=16) ax2.grid('on') return (fig,ax1,ax2) Kdc = 2e5 B = 2*np.pi*1e6 tau_opamp = Kdc/B K = opamp_tf(B=B, Kdc=Kdc) Utau = 500e6 L = 100e6 Rs = 0.2 C = 47e6 U = ugly_tf(L=L, Rs=Rs, C=C, tau=Utau) R = 100e3 Cb = 1e6 tau_fb = R*Cb F = lambda s: (U(s)+R*Cb*s)/(1+R*Cb*s) loop1 = lambda s: K(s)*U(s) loop2 = lambda s: K(s)*F(s) fig = plt.figure(figsize=(10,10)) _, ax1, ax2 = bodeplot([loop1, loop2], fig=fig, maglimits=[0,None]) for ax in [ax1,ax2]: ax.legend(['K*U','K*F'])
There’s a big difference between the loop gain with the feedback bypass capacitor (KF) and the loop gain without it (KU). Without the feedback bypass capacitor, we have a really ugly transfer function with more than 180 degree phase lag long before it gets to unity gain (and nearly 360 degree phase lag at unity gain). With the feedback bypass capacitor, we can achieve essentially a firstorder transfer function for openloop gain.
Just because the openloop gain is well behaved, it doesn’t mean the closedloop transfer function is very smooth, but it does ensure stability. Let’s draw a Bode plot of the closedloop transfer function. We’ll also use the scipy.signal.lti
objects to show step responses (although there’s some algebra to get this).
# closedloop transfer functions cloop1 = lambda s: K(s)*U(s)/(1+K(s)*F(s)) Uden = lambda s: 1/U(s) cloop2 = lambda s: Kdc*(1+tau_fb*s)/(Uden(s)*(tau_opamp*s+1)*(tau_fb*s+1) + Kdc*(1 + tau_fb*s*Uden(s))) cloopdiff = lambda s: cloop1(s)cloop2(s) def maxdiff(f1,f2,x): return np.max(np.abs(f1(x)  f2(x))) # are these the same transfer function? (check my algebra) stest = 1j*np.logspace(6,6,1000) print "cloop1cloop2:", maxdiff(cloop1, cloop2, stest) import scipy.signal from numpy.polynomial.polynomial import Polynomial P1 = Polynomial(1) # 1/(tau*s + 1)/(L*C*s*s + Rs*C*s + 1) pUden = P1 * [1,Utau] * [1,Rs*C,L*C] # scipy.signal.lti expects *descending* order def make_closedTF(tau): tau_s = 0 if tau is None else Polynomial([0,tau]) return scipy.signal.lti(# Numerator (P1 * Kdc * (tau_s + 1)).coef[::1], # Denominator ( (pUden * [1,tau_opamp] * (tau_s + 1)) + Kdc * (1 + pUden*tau_s) ).coef[::1] ) closedTF = make_closedTF(tau_fb) closedTF_H = lambda s: closedTF.freqresp(s/1j)[1] print "cloop1LTI", maxdiff(cloop1, closedTF_H, stest)
cloop1cloop2: 6.66200242854e16 cloop1LTI 1.26858466865e15
fig = plt.figure(figsize=(10,10)) bodeplot([cloop1, cloop2, closedTF_H], fig=fig, maglimits=[60,None])
(<matplotlib.figure.Figure at 0x14f1d7050>, <matplotlib.axes._subplots.AxesSubplot at 0x14f1d7910>, <matplotlib.axes._subplots.AxesSubplot at 0x140dc0c50>)
This closedloop transfer function is mostly wellbehaved, although it does have a resonance due to the LC circuit. Here are step responses for various values of \( \tau = RC_b \):
fig=plt.figure(figsize=(10,8)) ax=fig.add_subplot(1,1,1) taulist = tau_fb * np.array([0.0005, 0.001, 0.01, 0.1, 1.0]) colors = 'gykbr' for k,tauval in enumerate(taulist): closedTF = make_closedTF(tauval) t,x=closedTF.step(T=np.arange(0,0.01,0.00001)) ax.plot(t,x, color=colors[k]) ax.legend(['%g us' % (tau*1e6) for tau in taulist]) ax.grid('on') ax.set_xlabel('t') ax.set_ylabel('y')
<matplotlib.text.Text at 0x146b3d6d0>
Essentially we have a tradeoff; by increasing the time constant (larger feedback bypass capacitance) we get slower response, but more stable. The circuit exhibits a step response with ringing with small feedback capacitance, and is unstable with no feedback capacitance.
Other examples
This technique is very powerful; I’ve used it several times in my career. One memorable occasion was a battery charger using a 95V Vicor FlatPAC switching power converter. (Designs I created using the Vicor converters both predated and followed the notorious William Tango Foxtrot design I wrote about a couple of years ago.) The FlatPAC converters have a trim circuit that allows trimming between 50% and 110% of nominal output voltage:
Unfortunately the trim circuit was originally intended to be a manual, potentiometerbased voltage adjustment. Vicor now has an application note on using it as a constantcurrent battery charger; at the time I created my design, they had some appnote about constantcurrent use, but I don’t think it went into as much detail, and I had to figure out the hard way that the trim pin had a nonlinear, bandwidthlimited response that was not welldocumented, so I used an opamp to control the trim pin, and in went the feedback bypass capacitor to save the day.
Opamps have these feedback capacitors, too!
You won’t just find this used in application circuits. In fact, almost all opamps have an internal feedback bypass capacitor for frequency compensation; here’s the design of the ubiquitous (and awful! please use a better one!) 741 opamp:
(Image from Wikipedia)
The 30pF capacitor is an internal feedback capacitor that reduces the highfrequency gain of the opamp, in order to allow the amplifier to have a predictable transfer function that is stable at unitygain.
Isn’t it just an integrator?
Another way of thinking about the RC stabilizing technique is that it forms an integrator:
If you go through the circuit analysis, you can derive that the gain from \( V_{out} \) to the output of the opamp is $$G(s) = \frac{K}{1 + (1+K)RC_b s}$$
If the opamp gain K is large, then \( G(s) \approx 1/RC_b s \) and we have an ideal integrator… but the opamp’s lowfrequency gain isn’t infinite. A Bode plot of the integrator’s transfer function (with the sign reversed) versus the opamp’s openloop gain shows that we’re basically just creating a “virtual” opamp with much lower gainbandwidth product:
fig = plt.figure(figsize=(10,10)) K = opamp_tf(B=B, Kdc=Kdc) integ = lambda s: K(s)/(1 + R*Cb*s + K(s)*R*Cb*s) _, ax1, ax2 = bodeplot([H1, integ], fig=fig, maglimits=[60,None], xlim=[1e6,1e6]) for ax in [ax1,ax2]: ax.legend(['K (opamp)','RC integrator'])
The analysis above isn’t completely correct, however; it shows the feedback loop transfer function, but neglects the transfer function from the \( + \) terminal of the opamp to its output, which we can add:
$$ V_{opamp,out} = \frac{KV_{out} + K(1 + RC_b s)V_{in}}{1 + (1+K)RC_b s} $$
This has an additional zero for the transfer function from \( V_{in} \) to opamp output, which essentially creates a gain of 1 between the cutoff frequency \( \omega_c = 1/RC_b \) and the opamp’s gainbandwidth.
Wrapup
Feedback bypass capacitors are really useful! They let you use an opamp to take lowfrequency feedback from some ugly, nonlinear, slow system, but stabilize the control loop by taking highfrequency feedback from the opamp output itself, forming a unity gain at high frequencies. I recommend putting this in your bag of circuitdesign tricks.
On a related subject: Jim Williams has a nice writeup called “The Oscillation Problem (Frequency Compensation without Tears)” in Linear Technology’s AN18 that gives some good practical advice without getting into any control theory.)
Happy New Year!
P.S. I need to point out another entry to the Hall of Shame. While doing some additional research for this article, I looked up National Semiconductor’s Application Note 4: Monolithic Op Amp — The Universal Linear Component, written by legendary chip designer Bob Widlar and published in April 1968. National was acquired by Texas Instruments in 2011, and you can still find AN4, but it’s been “revised” as TI’s SNOA650B. (I managed to find an earlier version of the National PDF on a webpage at Colorado State University.) The “revised” version omits any mention of Widlar as author of the appnote, and the documentation dwarves at TI have assiduously replaced “National Semiconductor” with “Texas Instruments”… but haven’t bothered to “revise” any other aspects of the appnote, including the fact that the abstract says
The cost of monolithic amplifiers is now less than \$2.00, in large quantities, which makes it attractive to design them into circuits where they would not otherwise be considered.
and later on
Although the designs presented use the LM101 operational amplifier and the LM102 voltage follower produced by Texas Instruments
Hello… it’s 2017, not 1968; I can buy an LM358 opamp for less than 10 cents in 1K quantities, and there are no more LM102 voltage followers; they were long gone before TI ever got their hands on National. Someone bothered to change that sentence to say “Texas Instruments” rather than “National Semiconductor” but didn’t bother to check it for factual correctness.
I like many of TI’s chips, and am amazed at what kind of clever products come out of their factory, but I really wish they would have just published the old National appnotes in their original form, as historical — but still invaluable — documents. The national.com
website is gone, and sorely missed. It makes me wish I had kept some of those databooksonCDROM that the semiconductor manufacturers put out in the late 1990’s, in those few years between the era of paperweight databooks and the era of ephemeral publications on the Internet.
I hope TI will correct this injustice.
© 2017 Jason M. Sachs, all rights reserved.
 Comments
 Write a Comment Select to add a comment
I've tried to download as many databooks as I can find in PDF format. I keep them on a microSD card that I can use in my tablet. I have found many at: https://archive.org/details/bitsavers
Jason
"It makes me wish I had kept some of those databooks..."
For what it's worth I preserved all my old data by scanning the data books. I am happy to send the archive to anyone. You can read about it in my blog "Preserving Data Books From Yesteryear". Incidentally although it's not clear in the blog, if you click on the National data book image, it will take you to a sequence of my impression of art on different data book covers.
I always called that process compensation, not bypass.
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: