Sunday, January 5, 2014

The Twin-T filter


Today, only a little bit of breadboarding and some maths ...

While browsing the web to try to learn about oscillators, I found this cool circuit:




I built it in LTSpice and the simulation says vOut is supposed to do  this:



It not a perfect sine wave, but it's quite close, and LTSpice predicts it should oscillate at around 136 Hz:





Something nice about this circuit is that needs only 4 resistors, 3 caps and an 1 NPN BJT transistor.

In particular this oscillator does not need any inductors (inductors are bulky, expensive and prone to EM noise).

So, let's build this circuit on the breadboard and check what happens:



As you can see on the pic, it is quick and easy to put together.

Let's power it up, hook up the 2N2222 collector to the scope to see what voltage at vOut looks like:


So the real world voltage for this circuit looks very much like what LTSpice predicted.

In particular, the scope curve has the exact same little bump at the botttom left of the sine wave as the one in the simulation.

Scope also says oscillation frequency is stable, at around 140.8Hz. This is not too far off what the frequency LTSpice predicted (the components I used to build it aren't precision, so no surprise if predicted frequencies and actual aren't exactly equal).

Now, I am trying to understand why it oscillates, and that is not exactly intuitively obvious.

I'm attacking this is by looking at the 2N2222 NPN.

The transistor basically acts like a valve that controls the amount of current flowing from its collector to its emitter.

Current is pushed by a constant voltage source through a 1k resistor down into the collector of the transistor , and the current coming out of the emitter of the transistor goes directly to ground.

Basic property of BJT transistors: the amount of current flowing from collector to emitter is in turn  more or less proportional to the signal applied to the transistor base.

In this circuit, that signal happens to be taken from the collector voltage itself, first piped through a "feedback network" and injected into the transistor base. 

So, the magic clearly happens in the feedback network.

Let's take a closer look at what that looks like:


After some quality time with Google and the help of a kind soul at electronics.stackexchange.com, I found out that this sub-circuit is called a "Twin-T filter" because it looks like two "T" shaped filters (at top and bottom) combined into a large filter.

The Twin-T filter (wikipedia) is a so-called "notch filter": it lets almost all frequencies through, with the exception of a small band of frequencies that get strongly attenuated (the frequencies within the "notch", hence the name).

To intuitively understand how the notch filter works, you have to look at it as two simple T-shaped filters connected in parallel.

The top part (the top "T") with the two 1k resistors and the 2.2μF is a low-pass filter:
  • At high frequencies, the vertical cap conducts and dumps everything to ground. High frequencies therefore do not make it through the filter.
  • On the other hand, at low frequency the vertical cap is highly resistive and these frequencies pass through the horizontal resistors.
The bottom part with the two 2.2μF is a high-pass filter:
  • At high frequencies, the horizontal caps conduct and allow most of the signal to make it through safe a little bit that dumps to ground through the 100Ω resistor.
  • At low frequency the two horizontal caps are highly resistive and nothing makes it through.
Since these two filters are connected in parallel, they behave as a logical "OR":

  • low frequencies pass almost unhindered through the top part
  • high frequencies pass almost unhindered through the bottom part
  • a small subset of frequencies is neither making it through the top nor the bottom.
One way to verify this intuition is to ask LTSpice to run an AC analysis of that specific portion of the circuit.

Lo and behold, we see the notch clearly: the filter does indeed drop only frequencies in a narrow band around 140Hz:



The graph above is a "Bode Plot" : it has

  • frequencies on the X axis
  • signal amplitude attenuation on the left Y axis (the V-shaped curve)
  • "phase shift" on the right Y axis (the S-shaped curve).

Both attenuation (left Y axis) and frequencies (X axis) have a logarithmic scale.

What we notice is that frequencies which are filtered out (attenuated) the strongest are also "phase-shifted", or "delayed" the most, with the maximum shift (180 degrees) more or less aligned with the tip of the notch, where the attenuation is also maximum.

It turns out  - and a tad counter-intuitively so - that the frequency that is maximally attenuated is the one and only frequency that will eventually "remain" in the collector->emitter branch of the circuit.

Go figure ...

The explanations I've read so far is that it is because that specific frequency is the most shifted/delayed (hence the name "phase-shift oscillator"), but to be honest, I'm still trying to wrap my head around this one from an intuitive point of view.

Let's shift gear and look at the math: time to pull out Mathematica and dig a little deeper.



To understand the Twin-T filter, we model the topology with completely general impedances and we perform a full nodal analysis:

$z_{ij}$ denotes impedances from node $i$ to node $j$
$i_{ij}$ denotes currents from node $i$ to node $j$
$v_{i}$ denotes voltage between node and ground $i$


   Twin T filter : H topology with grounded center:
   ================================================

                          Voltage Source

                is0         vs    vg        i2g
      +----------<----------+     +----------<----------+
      |                                                 |
      |         i01       i01  v1 i12       i12         ^
      |     +---->---z01--->---+--->---z12--->----+     |
      |     |                  |                  |     |
  is0 V     |              i14 v                  |    z2g (load)
      |     |                  |                  |     |
      |     ^ i01             z14            i12  v     |
      |     |                  |                  |     ^
      |     |              i14 v                  |     |
      |     |                  |  i4g             |     |
      +-->--+ v0            v4 +--->---+ vg    v2 +-->--+
       is0  |                  |                  | i2g
            |                  v i43              |
            |                  |                  |
        i03 v                 z43                 ^ i32
            |                  |                  |
            |                  v i43              |
            |                  |                  |
            +---->---z03--->---+--->---z32--->----+
                i03       i03  v3 i32       i32


Step 0 : dumb, brute force application of the nodal analysis method gives us 16 equations:

\[ \eqalign{ \left( \begin{array}{c} \text{}\\ \underline{\text{Constant voltages}}\\ \text{vg}=0 & \text{ground is at zero volt}\\ \text{v0}=\text{vs} & \text{constant source voltage}\\ \text{v4}=\text{vg} & \text{center of twin-t is grounded}\\ \text{}\\ \underline{\text{Ohm's law on circuit branches}}\\ \text{i01}=\frac{\text{v0}-\text{v1}}{\text{z01}} \\ \text{i12}=\frac{\text{v1}-\text{v2}}{\text{z12}} \\ \text{i32}=\frac{\text{v3}-\text{v2}}{\text{z32}} \\ \text{i03}=\frac{\text{v0}-\text{v3}}{\text{z03}} \\ \text{i14}=\frac{\text{v1}-\text{v4}}{\text{z14}} \\ \text{i43}=\frac{\text{v4}-\text{v3}}{\text{z43}} \\ \text{i2g}=\frac{\text{v2}-\text{vg}}{\text{z2g}} \\ \text{}\\ \underline{\text{Kirchoff Current Law at circuit nodes}}\\ \text{i2g}+\text{i4g}-\text{is0}=0 \\ \text{is0}-\text{i03}-\text{i01}=0 \\ \text{i01}-\text{i12}-\text{i14}=0 \\ \text{i12}-\text{i2g}+\text{i32}=0 \\ \text{i03}-\text{i32}+\text{i43}=0 \\ \text{i14}-\text{i43}-\text{i4g}=0 \\ \end{array} \right) } \]

Step 1 : get rid of  the 7 obvious current variables using equations from the Ohm's law section:

\[\left( \begin{array}{c} \text{vg}=0 \\ \text{v0}=\text{vs} \\ \text{v4}=\text{vg} \\ \text{i4g}+\frac{\text{v2}-\text{vg}}{\text{z2g}}=\text{is0} \\ \text{is0}+\frac{\text{v1}-\text{v0}}{\text{z01}}+\frac{\text{v3}-\text{v0}}{\text{z03}}=0 \\ \frac{\text{v0}-\text{v1}}{\text{z01}}+\frac{\text{v2}-\text{v1}}{\text{z12}}+\frac{\text{v4}-\text{v1}}{\text{z14}}=0 \\ \frac{\text{v1}-\text{v2}}{\text{z12}}+\frac{\text{v3}-\text{v2}}{\text{z32}}+\frac{\text{vg}-\text{v2}}{\text{z2g}}=0 \\ \frac{\text{v0}-\text{v3}}{\text{z03}}+\frac{\text{v2}-\text{v3}}{\text{z32}}+\frac{\text{v4}-\text{v3}}{\text{z43}}=0 \\ \frac{\text{v1}-\text{v4}}{\text{z14}}+\frac{\text{v3}-\text{v4}}{\text{z43}}=\text{i4g} \\ \end{array} \right) \]

Step 2 : get rid of the 3 obvious voltage variables using equations from the constant voltage section:

\[ \left( \begin{array}{c} \text{i4g}+\frac{\text{v2}}{\text{z2g}}=\text{is0} \\ \text{is0}+\frac{\text{v1}-\text{vs}}{\text{z01}}+\frac{\text{v3}-\text{vs}}{\text{z03}}=0 \\ \text{v1} \left(\frac{1}{\text{z01}}+\frac{1}{\text{z12}}+\frac{1}{\text{z14}}\right)=\frac{\text{v2}}{\text{z12}}+\frac{\text{vs}}{\text{z01}} \\ \frac{\text{v1}-\text{v2}}{\text{z12}}+\frac{\text{v3}-\text{v2}}{\text{z32}}=\frac{\text{v2}}{\text{z2g}} \\ \text{v3} \left(\frac{1}{\text{z03}}+\frac{1}{\text{z32}}+\frac{1}{\text{z43}}\right)=\frac{\text{v2}}{\text{z32}}+\frac{\text{vs}}{\text{z03}} \\ \text{i4g}=\frac{\text{v1}}{\text{z14}}+\frac{\text{v3}}{\text{z43}} \\ \end{array} \right) \]


Step 3 : with a little more work, eliminate $v1, v3, is0, i4g$ leaves us with two equations:


\[ \left( \begin{array}{c} \text{v2} \left(\frac{\text{z01}+\text{z14}}{\text{z01} (\text{z12}+\text{z14})+\text{z12} \text{z14}}+\frac{\text{z03} (\text{z2g}+\text{z32}+\text{z43})+\text{z43} (\text{z2g}+\text{z32})}{\text{z03} \text{z2g} (\text{z32}+\text{z43})+\text{z2g} \text{z32} \text{z43}}\right)+\text{vs} \left(-\frac{\text{z14}}{\text{z01} (\text{z12}+\text{z14})+\text{z12} \text{z14}}-\frac{\text{z43}}{\text{z03} (\text{z32}+\text{z43})+\text{z32} \text{z43}}\right)=0 \\ \frac{-\text{v2} \text{z43} (\text{z01} (\text{z03}+\text{z12}+\text{z14}+\text{z32})+\text{z14} (\text{z03}+\text{z12}+\text{z32}))-\text{v2} \text{z03} (\text{z01} (\text{z12}+\text{z14}+\text{z32})+\text{z14} (\text{z12}+\text{z32}))+\text{vs} \text{z43} (\text{z01} (\text{z12}+\text{z14})+\text{z14} (\text{z03}+\text{z12}+\text{z32}))+\text{vs} \text{z03} \text{z14} \text{z32}}{(\text{z01} (\text{z12}+\text{z14})+\text{z12} \text{z14}) (\text{z03} (\text{z32}+\text{z43})+\text{z32} \text{z43})}=\frac{\text{v2}}{\text{z2g}} \\ \end{array} \right) \]

Even if it isn't obvious at first glance, it turns out that last two equations are in fact equivalent and Mathematica confirms it ... we probably introduced a redundant equation somewhere in the initial mix, no biggie. As long as they are equivalent and don't produce a contradiction, we can just user either of the two.

We pick the first one and ask Mathematica to solve for $v2$:

\[ \text{v2}=vs\times \frac{\text{z2g}.(\text{z14}.\text{z43}.(\text{z01}+\text{z03}+\text{z12}+\text{z32})+\text{z01}.\text{z12}.\text{z43}+\text{z03}.\text{z14}.\text{z32})}{\text{z01}.\text{z43}.(\text{z03}.(\text{z12}+\text{z14}+\text{z2g})+\text{z32}.(\text{z12}+\text{z14}+\text{z2g})+\text{z2g}.(\text{z12}+\text{z14}))+\text{z01}.\text{z03}.(\text{z32}.(\text{z12}+\text{z14}+\text{z2g})+\text{z2g}.(\text{z12}+\text{z14}))+\text{z14}.\text{z43}.(\text{z03}.(\text{z12}+\text{z2g})+\text{z32}.(\text{z12}+\text{z2g})+\text{z12}.\text{z2g})+\text{z03}.\text{z14}.(\text{z12}.(\text{z2g}+\text{z32})+\text{z2g}.\text{z32})} \]


Not unexpectedly so, $v2$ still depends on $z2g$, the load impedance.

We need to eliminate that if we want to isolate the intrinsic behavior of the Twin-T.

One way to get rid of $z2g$ is by another application brute force: we just ask Mathematica to take the limit $z2g\to\infty$, which is the math equivalent of physically disconnecting the load.

The result is quite nice: we get $v_2 = Z \times v_s$, where the proportionality constant $Z$ is only a function of the impedances $z_{ij}$:

\[ \boxed{
Z=\frac{\text{z14}.\text{z43}.(\text{z01}+\text{z03}+\text{z12}+\text{z32})+\text{z01}.\text{z12}.\text{z43}+\text{z03}.\text{z14}.\text{z32}}{\text{z01}.\text{z43}.(\text{z03}+\text{z12}+\text{z14}+\text{z32})+\text{z01}.\text{z03}.(\text{z12}+\text{z14}+\text{z32})+\text{z03}.\text{z14}.(\text{z12}+\text{z32}+\text{z43})+\text{z14}.\text{z43}.(\text{z12}+\text{z32})} } \]

This is neat: we have calculated the general formula for the impedance of the feedback network as a function of only the impedances of each branch.

Now, let's replace the general impedances by actual caps and resistors.

To keep the size of math expressions under control, we reduce the number of degrees of freedom a little by making the top two resistors equal and the two bottom caps equal and we keepi separate values for the middle branch cap and resistor.

This is unfortunately a loss of generality, but even with this restriction, the maths are already getting kind of heavy ... if we crack this restricted version, we'll try to revisit the general case.

So, we perform the following substitutions:

\[ \eqalign{ \text{z01}&\to r \\ \text{z12}&\to r \\ \text{z14}&\to \frac{1}{i\omega \text{c14}} \\ \text{z43}&\to \text{r43} \\ \text{z03}&\to \frac{1}{i\omega c} \\ \text{z32}&\to \frac{1}{i \omega c} \\ } \]

$Z$ becomes:

\[
Z=\frac{Re + i\times Im}{Den}
\]

with:

\[
\eqalign{
\text{Re}&=
c.\omega^2.\left(r^2.\left(4.c^3.\text{r43}^2.\omega^2+c.\text{c14}^2.r.\text{r43}.\omega^2.\left(c^2.r.\text{r43}.\omega^2-1\right)-\text{c14}\right)+4.c.\text{r43}^2\right)+1
\\
\text{Im}&=
c^3.\text{c14}^2.r^3.\text{r43}.\omega^5.(r+2.\text{r43})+4.c^2.r.\text{r43}.\omega^3.(c.r-\text{c14}.\text{r43})-r.\omega.(2.c+\text{c14})
\\
\text{Den}&=
c^4.\text{c14}^2.r^4.\text{r43}^2.\omega^6+c^2.r^2.\omega^4.\left(4.\text{r43}^2.\left(c^2+c.\text{c14}+\text{c14}^2\right)+\text{c14}^2.r^2+2.\text{c14}^2.r.\text{r43}\right)+\omega^2.\left(4.c^2.\left(r^2+r.\text{r43}+\text{r43}^2\right)+2.c.\text{c14}.r^2+\text{c14}^2.r^2\right)+1
}
\]

That's quite a mouthful, but we can already tell that the denominator can never go to zero as long as $\omega, r, c, r43, c14$ are all positive. In other words, the filter has no real, positive poles.

Now, we'd like to know exactly how much a given frequency is affected by the filter. For this, we need to calculate the modulus of the transfer function $|Z|$ and sniff out its zeroes and/or extremas.

As a matter of fact, let's compute the modulus squared: it is enough for our purpose of finding zeroes and extremas since square root is monotonous and vanishes with its argument.

\[
(Re^2 + Im^2) = \frac{
c^2 \text{r43}\omega^2\left(r \left(c.r.\text{r43}\omega^2\left(c\left(\text{c14}^2 r^2
\omega ^2+4\right)-4\text{c14}\right)-4\right)+4 \text{r43}\right)+1}{c^4 \text{c14}^2
r^4\text{r43}^2\omega^6+c^2 r^2\omega^4
\left(4 \text{r43}^2 \left(c^2+c.\text{c14}+\text{c14}^2\right)+\text{c14}^2 r^2+2
\text{c14}^2 r.\text{r43}\right)+\omega ^2 \left(4 c^2\left(r^2+r.\text{r43}+\text{r43}^2
\right)+2 c.\text{c14} r^2+\text{c14}^2 r^2\right)+1}
\]

Now that we have derived a formula for the modulus, let's do a quick sanity check.

Using the caps and resistor values from the beginning of this post, let's ask Mathematica to compute the Bode plot for us to see if it looks like what LTSpice calculated.

The result is this (modulus is in blue, phase shift is in red):


Aside from the phase being displayed slightly different (Y axis is rotated modulo 360), it is the exact same thing LTSpice plotted for us earlier using simulation. In particular, the minimum is at the same spot, so there is a chance that we got the math right. Excellent !.

Next, let's work on that modulus squared expression to figure out how it behaves when $\omega$, $\text{r43}$ and $\text{c14}$ change.

The things we're interested in are:

  • Where is the notch located on the X axis (i.e. which frequency is most attenuated)
  • How "deep" the notch is (how much is the attenuation at the notch frequency)
  • How "wide" the notch is (how selective the filter is).

First thing we notice is that the expression only depends on $\omega^2$ and not on $\omega$.

This is good because the expression becomes simpler: we make a change of variable $\omega^2\to\omega 2$.

In other words, we'd like to compute where on the X-axis is the tip of the notch.

Next, we take the derivative w.r.t. $\omega2$ and try to find its zeroes to find frequencies at which the signal is maximally affected by the filter.

Calculations are starting to get heavy, but Mathematica helps.

Also, since we're looking for zeroes, we only need the numerator of the derivative.

We might have to go back in later to verify that the zeroes we pull out don't end up also being zeroes of the denominator, is all.

So, we ask mathematica to compute:

\[
Numerator[\frac{\partial\space(modulusSquared)}{\partial\space(\omega2)}]
\]

The result is a polynomial in $\omega2$ with the following coefficients:

\[
\begin{array}{ccc} \left\{ \eqalign{ -4.c^2.r^2-8.c^2.r.\text{r43}-2.c.\text{c14}.r^2-\text{c14}^2.r^2 & | & \omega2^0\\ -2.c^2.\text{c14}.r^2.\left(4.\text{r43}^2.(2.c+\text{c14})+\text{c14}.r^2+2.\text{c14}.r.\text{r43}\right) & | & \omega2^1\\
4.c^3.r^2.\text{r43}.(c.r-\text{c14}.\text{r43}).\left(-2.c.r.\text{r43}.(\text{c14}-2.c)+4.c.\text{r43}^2.(2.c+\text{c14})+\text{c14}^2.r^2\right) & | & \omega2^2 \\
2.c^4.\text{c14}^2.r^5.\text{r43}^2.\left(4.c^2.(r+2.\text{r43})+2.c.\text{c14}.r+\text{c14}^2.r\right) & | & \omega2^3\\
c^6.\text{c14}^3.r^6.\text{r43}^2.\left(4.\text{r43}^2.(2.c+\text{c14})+\text{c14}.r^2+2.\text{c14}.r.\text{r43}\right) & | & \omega2^4
}\right\} \end{array} \]

Again, quite a mean looking expression.

There's a few things we can do to simplify this.

First, looking at how the filter works, we can sort of "feel" that what matters is the proportion of current being diverted to ground in each of the low and high pass branch of the filter. This inspires us to try the following change of variable:
\[ \eqalign{ r43&\to&\frac{\alpha}{r}\\ c14&\to&\beta\times c\\ } \]

This gives us:

\[ \begin{array}{ccc} \left\{ \eqalign{ -\frac{c^2 r^2 (\alpha (\beta (\beta +2)+4)+8)}{\alpha } & | & \omega2^0\\ -\frac{2 \beta c^4 r^4 ((\alpha (\alpha +2)+4) \beta +8)}{\alpha^2} & | & \omega2^1\\ \frac{4 c^6 r^6 (\alpha -\beta )(\alpha(\beta(\alpha\beta-2)+4)+4(\beta+2))}{\alpha ^4} & | & \omega2^2\\ \frac{2 \beta ^2 c^8 r^8 (\alpha (\beta (\beta +2)+4)+8)}{\alpha ^3} & | & \omega2^3\\ \frac{\beta ^3 c^{10} r^{10} ((\alpha (\alpha +2)+4) \beta +8)}{\alpha ^4} & | & \omega2^4\\ }\right\} \end{array} \]

 This looks nice !

First thing we notice is we can get factor out $\frac{r^2c^2}{\alpha^4}$:

\[ \left( \begin{array}{ccc} -\alpha ^3 (\alpha (\beta (\beta +2)+4)+8) & | & \omega2^0\\ -2 \alpha ^2 \beta c^2 r^2 ((\alpha (\alpha +2)+4) \beta +8) & | & \omega2^1\\ 4 c^4 r^4 (\alpha -\beta ) (\alpha (\beta (\alpha \beta -2)+4)+4 (\beta +2)) & | & \omega2^2\\ 2 \alpha \beta ^2 c^6 r^6 (\alpha (\beta (\beta +2)+4)+8) & | & \omega2^3\\ \beta ^3 c^8 r^8 ((\alpha (\alpha +2)+4) \beta +8) & | & \omega2^4\\ \end{array} \right) \]

Next, we notice that another variable change $\omega2\to\frac{u}{r^2c^2}$ will simplify things further:

\[ \left( \begin{array}{ccc} -\alpha^3(\alpha(\beta(\beta +2)+4)+8) & | & u^0\\ -2\alpha^2\beta((\alpha(\alpha+2)+4)\beta+8) & | & u^1\\ 4(\alpha-\beta)(\alpha(\beta(\alpha\beta-2)+4)+4(\beta+2)) & | & u^2\\ 2\alpha\beta^2(\alpha(\beta(\beta+2)+4)+8) & | & u^3\\ \beta^3((\alpha(\alpha+2)+4)\beta+8) & | & u^4\\ \end{array} \right) \]

This is really cool, because at this point we've done away with all parameters except $\alpha$ and $\beta$ and we have a found out that the center of the notch obeys the following formula:

\[ notchFrequency = \omega/(2\pi) = \frac{\sqrt{F(\alpha,\beta)}}{2\pi r c} \]

Now, unfortunately, if we ask Mathematica for an explicit closed-form expression for $(\alpha, \beta)$, it yields a really nasty looking formula (very, very large).

In fact, because it's a degree four polynomial, it yields four very complicated formulas, and it's kind of hard to tell which - if any - is actually real and positive. Ugh.

Mmmh.

Ways to proceed at this point:

  • Draw a graph to see how the solution behaves as a function of $(\alpha, \beta)$.
  • Try to find ways to bind $\alpha$ and $\beta$ that yield simpler solutions

Let's try to draw the surface $u=Solution(\alpha, \beta)$ with some numerical values or $r$ and $c$ and see how it behaves.

Sunday, December 15, 2013

12VDC -> 220V AC step-up transformers, a.k.a inverters and laptop chargers don't always work together well.

The other day, I had to go on a long road-trip and wanted to make sure I would keep access to my laptop throughout the journey.

Without giving it much thought, I went to the car parts store and bought me one of these:





The idea was to convert the 12V DC output of my battery to a good old 230V AC that I could plug my 220V AC to 20V DC laptop adapter in.

First, it's kind of sad to jump through all these hoops just to do a 12VDC to 20VDC step-up, but I was in a rush.

Second, a bit overkill on the power rating side, but you never know what you'll end up needing to hook to your car battery when on a camping trip.

Well ... turns out buying this things was a bad idea: contrary to expectations, the laptop refused to charge.

Back home a couple of days later, I decided to investigate ...

First step, let's tear the box open to see what goes on in there:



At first glance,  the inside looks very much like most inverter circuit schematics you cand find on the net:
  • A DC input stage on the right and top with a bunch of what's likely foolproofing diodes and noise filtering caps.
  • A good old LM324N quad op-amp, maybe used to produce the AC oscillation
  • Two ICs that look like voltage regulators or voltage references, codename KA7500B
  • A bunch of power transistors hooked up to the case which doubles as a heat-sink. Transistors look like they are organized in push-pull configuration to current-amplify the oscillation.
  • A bunch of transformers to do the 12V -> 230V step-up.
  • A big fat 400V-rated electrolytic cap, not sure what for, maybe some more filtering.
  • The 230V AC output stage.
In other words, nothing fancy.

Yet things don't work.

Time too hook the whole thing up to the scope to see what's going on:


Now let's feed it 12VDC on the DC input side (current limited to 1A, you never know):



First thing I notice: the darn thing sucks up 8 Watts of power without any load on the AC side :-(
Don't let it hooked to your car battery with the engine off.

Anyhoo, continuing, Fluke says 12V are going in, everything looks good.


And let's take a gander a what comes out the other side:



Ahah.

And ugh.

So, sure enough, at first glance, we get "mains-like" juice:
  • 50Hz says the scope
  • 296V plus peak says the scope
  • -300V minus peak says the scope
  • 244V RMS says the scope (close enough to 230V)
But but but ... the likeness stops there: the waveform is not exactly what I'd call a nice sine wave ... it's in fact an awful bad PWM-like approximation of a sine wave. Yuck.

This is very likely where the problem comes from: I bet my laptop power adapter sorta expects a real sinusodial waveform coming in.

This half-assed approximation of a sine wave is probably making it choke and shut down.

Solution ?

I think there are a few possible avenues:
  • Hacking the beast above by adding a large filtering cap after the output. Not confident enough in my electronics skills at this stage, so not going to try it.
  • Buying a "pure sine wave" adapter. I guess that's what I should have done, had I thought of doing my research :(
  • Buying a DC-to-DC laptop adapter which will at any rate likely be more efficient and more silent (hopefully no large fan and heatsink). Probably something like that.
At any rate, caveat emptor:

If you plan to charge your laptop in your car with a power inverter, you had better triple-check that the so-called "AC output" of the inverter you plan to buy is actually compatible with the input expected by your laptop adapter.

Best way to do this ? Confirm with salesman, buy it, try it, bring back to salesman if it doesn't charge your laptop, iterate.

But don't do what I did and expect it to work simply because the inverter says "230V AC output" and the laptop adapter says "230V AC input".

Sunday, November 17, 2013

Arduino and DAC

My quest to implement a version of the analogWrite() function on the Arduino that actually behaves as the name advertises continues.

In my previous post, I built a 4-stage Sallen-Key low pass filter to convert the PWM output to a flat signal, but it had shortcomings:
  • Lots of components to get a flat signal
  • Only 8 bit of resolution means only 256 voltage levels (although this could probably be improved using PWM tricks).
  • Unbuffered output meant I'd have to add a couple of op-amps behind it to get it to behave like a stable voltage source.
Today, I am trying to do the same thing with a real digital to analogue converter (a DAC) : the Analog Device DAC8412FPZ.

I just got my hands on one of these chip, and this is what it looks like:


Here's the data sheet for this little guy.

Advantage of this chip:
  • 12 bit output resolution gives you 4096 possible output voltage levels.
  • 28-pin DIP packaging makes for easy breadboarding and experimenting.
  • 4 channels (effectively, the chip contains 4 quasi independent DACs) so you can send 4 independent analogue signals to 4 separate devices.
  • Works with both unipolar or bipolar power supplies in [5V, 30V] range means you can power it from an Arduino +5V supply for experiments but still move the final project to a proper bipolar +/- 12V supply later on.
  • Outputs are buffered and can source or sink +/- 5 mA at 10V, which means you don't have to add a power amplification stage after the DAC if all you want is to drive small loads.
  • Data transfers to the chip is done with a parallel bus, which is annoying to wire up, but super simple to manipulate in software.
Disadvantages of this chip:
  • Chip is kinda bulky (4cm x 1.5cm)
  • Unless you manage to get your hands on a sample, chip is expensive (~ $35)
  • The parallel bus is a bit of a pain to wire up (12 wires) and you need a lot of pins on the micro controller unit side. 
This last part is actually the most annoying: you are going to need an Arduino Mega if you want enough digital ports to control this thing directly.

Here's  what it looks like all wired up on the breadboard:



A couple of things to note:
  • The big bundle of green wires on pin 30 to 41 of the Arduino (12 of them) is the data bus and it's the reason why you need a Mega to makes this work :(.
  • The two blue wire are the "address bus", that you use to select which of the 4 DACs you're fiddling with.
  • In case you're wondering what they're for, some patch cables are only in there to mechanically anchor the Mega to the breadboard.
  • The Arduino's digital pins 7, 6, 5, 4, 3, 2 are used to manipulate the control pins of the DAC.
  • The Arduino, via its A0 analog pin, is also used to read back the voltage output of the DAC to check that it all works (we only get a resolution of 1024 levels since the embedded ADC of the Arduino only does 10bits).
  • In the pictures, the DAC is powered by an external +/- 12V power supply. For this, I use this excellent little breakout board from the good folks at dangerous prototypes which neatly converts the output of an old PC ATX power supply I had lying around.
  • In this example, the VRefHigh and VRefLow pins of the DAC are hooked respectively to the +5V and GND of the Arduino, which means the DAC will output 4096 levels in the [0, 5V] interval. This makes it easy to read back the DAC's output voltage directly with the Arduino. However, you could hook these two pins to any other external reference (as long as VRefLow is less than VRefHigh and they're smaller than the power voltages), including making VRefLow negative to get the DAC to crank out negative voltages.
On the software side, here is a simple Arduino sketch that will:
  • Setup the Arduino pins to control the DAC
  • Reset the DAC
  • Control the DAC to repeatedly output all possible values in [VRefLow, VRefHigh] on all 4 channels at once
  • Use pin A0 to read back one of the DAC's voltage output
  • Dump the value read on the serial port.

Here is the code:

 // DAC8412FPZ  
   
 #define ADDR_BIT0 7 // Control pin got LSB of address bus  
 #define ADDR_BIT1 6 // Control pin got MSB of address bus  
 #define DATA_BIT0 30 // Control pin for LSB of data bus  
   
 #define RW 5 // Pin for read-write command : High -> read, Low -> write  
 #define CSB 4 // Pin for chip select : High -> Off, Low -> On. Chip ignores commands unless this is low  
 #define RSTB 3 // Pin to Reset all channels to midscale : High -> do nothing, Low -> do reset  
 #define LDACB 2 // Ping to control "Load DAC"  
   
 #define analogPin A0  
 static uint32_t count = 0;  
   
 // Prep the address bus to talk to one DAC  
 static inline void setAddrBus(  
   int dacId  
 )  
 {  
   digitalWrite(ADDR_BIT0, (dacId & 0x1) ? HIGH : LOW);  
   digitalWrite(ADDR_BIT1, (dacId >> 0x1) ? HIGH : LOW);  
 }  
   
 // Prep the data bus with a given value  
 static inline void setDataBus(  
   int16_t value  
 )  
 {  
   for(int i=0; i<12; ++i) {  
     digitalWrite(  
       i + DATA_BIT0,  
       (value & (1<<i)) ? HIGH : LOW  
     );  
   }  
 }  
   
 // Manipulate the "chip select" signal  
 static inline void ioState(  
   bool on  
 )  
 {  
   digitalWrite(CSB, on ? LOW : HIGH);  
 }  
   
 // Load a value on all 4 DACs  
 static inline void loadValue(  
   int16_t value  
 )  
 {  
   for(int i=0; i<4; ++i) {  
     ioState(false); delay(10);  
       setAddrBus(i); delay(10);  
       setDataBus(value); delay(10);  
     ioState(true); delay(10);  
     ioState(false); delay(10);  
   }  
 }  
   
 // Read a value from A0, output on serial  
 static inline void output()  
 {  
   uint16_t value = analogRead(analogPin);  
   Serial.print("count:");  
   Serial.print(count);  
   Serial.print(" millis:");  
   Serial.print(millis());  
   Serial.print(" value:");  
   Serial.println(value);  
 }  
   
 void setup()  
 {  
   // Prep control pins  
   pinMode( RW, OUTPUT);  
   pinMode( CSB, OUTPUT);  
   pinMode( RSTB, OUTPUT);  
   pinMode( LDACB, OUTPUT);  
   pinMode(ADDR_BIT0, OUTPUT);  
   pinMode(ADDR_BIT1, OUTPUT);  
   for(int i=0; i<12; ++i) {  
     pinMode(i + DATA_BIT0, OUTPUT);  
   }  
   
   // Reset DAC  
   digitalWrite(RSTB, LOW); delay(10); // Turn reset on  
   digitalWrite(RSTB, HIGH); delay(10); // Turn reset off  
   
   // Prep serial for output  
   Serial.begin(230400);  
 }  
   
 void loop()  
 {  
   loadValue(count*10);  
   output();  
   ++count;  
 }  
Here is the code on github.
And finally, here's the oscilloscope output:
Looks neat :)

Saturday, November 9, 2013

Active low-pass filtering of Arduino's pwm output for a clean, flat, software adjustable signal.

The rather ill-named Arduino's analogWrite() function absolutely does not do what its name suggests: you'd expect it to set the voltage one of the output pins to the given value.

To do this you'd typically need something called a "digital to analog converter", or DAC in short, something the Arduino unfortunately does not come equipped with.

Instead, analogWrite() generates a PWM signal,  i.e. a square wave with "ON" and "OFF" timings designed such that the *average* voltage value is what you requested (the so-called duty cycle of the signal).

This is fine to - say - drive a motor, but certainly doesn't help if you want a clean, flat, noiseless signal.

In fact, the whole premise of PWM is the assumption that the loads it's ever going to drive have a built-in filter. In the case of a motor, the slow response times of the physical device is the filter in question.

But for some applications, PWM just does not cut it: sometimes, you'd like your Arduino to produce a clean, flat, noiseless constant voltage signal that can be controlled in software.

The standard RC low pass filter (such as the one described in this article) kinda works for this and is super easy to build, but it generates a fairly noisy output signal.

For example, below is the standard (490Hz / 50% duty cycle) PWM output of the Arduino fed through such a RC low pass filter:




Bottom line, the output is better than the PWM wave, but still awful choppy.

A larger cap, or maybe a multi-stage RC low-pass filter would of course help smooth out the noise further, but:
  • you might not have larger caps handy
  • you'll still get fairly fat ripple
Here's for example a 4 stage RC low-pass filter.
The output still has ripples:


Looking at the components I have lying around, I tried to put something together to produce a flatter, software adjustable voltage level from my Arduino.

Turns out I had a LM324N quad op-amp, a bunch of caps and resistors, generally enough to put together a four stage active low pass filter, with each stage based on the Sallen-Key Low Pass topology.

I didn't really spend much time doing theoretical analysis of the circuit to figure our caps values, but rather played with what I had lying around until I found a combo that worked well with the signal produced but the Arduino.

Here's what the circuit looks like:



Note: LTSpice does not come standard with models for the LM324, and if you want to play with the circuit above, you'll need to add two files to your LTSpice lib directory (mine happens to be in ~/.wine/drive_c/Program Files (x86)/LTC/LTspiceIV/lib/sym/Opamps , but YMMV).

Put this SYM file in lib/sym/OpAmps
Put this MOD file in lib/sub

This last circuit has a number of advantages:
  • The output is a lot cleaner than the RC filter, and cleanly maps the [0 - 255] range of the analogWrite() function to the full [0-5V] range.
  • Because it is a 12V powered, active circuit, it can easily be hacked  to produce 255 clean levels in [0- X volts] where X is the max output voltage of your OpAmps. All that's needed is a gain bridge on the feedback loop of the 4th stage, and if you are using an OpAmp that can goes rail to rail (which the 324N isn't), you can sweep the full 12V range.
  • I've tried to measure the theoretical output noise in LTSpice, and looks like it is in the 10µV range ... well below the noise level generated by my 12V power supply.
Here's what the thing looks like on a breadboard:



Note that:
  • the quad op-amp is the IC in the center
  • each of the stages is laid out vertically around the op-amp
  • I've added a 330Ω + LED load at the output.
Here's an oscilloscope screenshot of the signal output:



Good enough for me.