Low-Sensitivity Sallen-Key Filter Design with the HP-41C Programmable Calculator
December 21, 2008
This program, which I originally wrote for the HP-67 calculator, addresses the problem of designing second order single op-amp low- and high-pass filters using the Sallen-Key topology.
The Sallen-Key filter topology has the advantage of using a minimum of components. The simplest Sallen-Key filters use only two resistors and two capacitors. Additional resistors may be added for input attenuation (low-pass only) and gain adjustment. The following schematics illustrate these generalized Sallen-Key circuits:
The equations governing the low-pass filter are as follows,
where f is the filter’s cut-off frequency, Q is its “quality”, and H is its gain at the cut-off frequency. The corresponding equations for the high-pass filter are:
The mathematically inclined will notice that in each case, there are three equations in either nine or ten variables. Thus there is no single “right” solution. At least six or seven of the variables have to be decided arbitrarily (f, Q, H, and three or four component values), at which point the remaining variables (component values) can be solved for. An article by Texas Instruments suggests a number of simplifications to help one choose component values, but this just adds the complication of which simplification to choose.
I recently came across a pair of application notes by National Semiconductor which gives a procedure for designing Sallen-Key filters to minimize the effect of component value tolerances on the performance of the filter. A side-effect of this procedure is to reduce the number of inputs to five: f, Q, H, RF, and R, where the latter is simply an indication of the magnitude of resistor values desired for R1, R2, and R3. The procedure then dictates how all the other values are chosen, even adjusting for available “real-world” values part way through the solution.
The program presented here implements this procedure, with some minor changes:
-
Instead of asking for a desired resistor magnitude, R, the program asks for a capacitor magnitude, C, since in my experience, the capacitors drive the design.
- The formula given for internal gain variable K in the procedure (please refer to the application note) seems to have been derived empirically, and has a jump at Q = 1.1. To make the formula simpler to implement, I modified it slightly to:
The graphs of the original (pink) and revised (blue) formulae show the difference. Testing has shown that the resulting solution is generally at most one real-world capacitor value increment different (with corresponding changes in resistor values of course).
Also, if H > K, then K is set equal to H, since otherwise it will not be possible to achieve the desired gain.
For the low-pass circuit, the desired gain, H, is achieved by a combination of input attenuation, α (controlled by R1 and R3), and the internal gain, K, of the filter (controlled by RF and RG). The division of this gain between the two stages depends on H and Q and is chosen to minimize the sensitivity of the circuit to component tolerances. For example, for H = 1 and Q = 2, the attenuator gain is α = 0.629 and the internal gain is K = 1.59, for a net gain of H = αK = 1.
The high-pass circuit has no attentuation stage, so H must be at least 1, and higher for Q > 0.917. If too low a value is entered for H, it is increased as necessary to make the circuit solvable.
For either circuit, to achieve a result with minimum component sensitivity without regard to gain, set H = 0. The program will automatically choose the optimal value for K (and thus H). The resulting gain will be output when determining the performance.
Using the Program
First type in the program and save it, or read it from a previously recorded magnetic card. The card should be labelled as follows:
LOW-SENSITIVITY SALLEN-KEY FILTER DESIGN | ||||
---|---|---|---|---|
f | Q | H | C | RF |
LP→RGC1C2R1R2R3 | HP→RGC1C2R1R2 | →f,Q,H |
Filter Design from Specifications
Example: Design a 500Hz low-pass unity-gain filter with a Q of 2, using capacitors in the 10nF range, and a 47kΩ resistor for RF:
Description | Keystrokes | Display |
---|---|---|
Use engineering notation | ENG 2 | 0.00 00 |
Enter f | 500 a |
500. 00 |
Enter Q | 2 b |
2.00 00 |
Enter H | 1 c |
1.00 00 |
Enter C | 10 EEx CHS 9 d |
10.0 -09 |
Enter RF | 47 EEx 3 e |
47.0 03 |
Compute RG | A | 79.5 03 |
Enter real-world RG and compute C1 | 82 EEx 3 R/S |
31.6 -09 |
Enter real-world C1 and compute C2 | 33 EEx CHS 9 R/S |
3.16 -09 |
Enter real-world C2 and compute R1 | 3.3 EEx CHS 9 R/S |
15.4 03 |
Enter real-world R1 and compute R2 | 15 EEx 3 R/S |
95.9 03 |
Enter real-world R2 and compute R3 | 100 EEx 3 R/S |
26.1 03 |
Enter real-world R3 | 27 EEx 3 R/S |
27.0 03 |
Notes
If a resistor is to be omitted (open circuit), this program displays a value of zero for the resistance. This is different than some of my other programs, which display a “large” value representing infinity.
When the value for RG is displayed as zero, meaning it can be omitted, the value of RF will not matter any more, and RF can be replaced by a direct connection.
Filter Performance from Chosen Components
During the calculation of the solution above, we’ve entered real-world values in response to each computed value. The real-world values of C1 and C2 are used when computing the values of R1, R2, and R3. However, the real-world values of each of those resistors does not affect the computed value of the remaining ones. Thus, the final filter may not perform exactly as specified. To find out how it does perform, follow these steps:
Description | Keystrokes | Display |
---|---|---|
Compute resulting f | E | 491. 00 |
Compute resulting Q | R/S | 1.86 00 |
Compute resulting H | R/S | 1.01 00 |
A High-Pass Example
Using the parameters already entered for the low-pass filter above, determine the components for a high-pass filter:
Description | Keystrokes | Display |
---|---|---|
Compute RG for high-pass filter | C | 79.5 03 |
Enter real-world RG and compute C1 | 82 EEx 3 R/S |
31.4 -09 |
Enter real-world C1 and compute C2 | 33 EEx CHS 9 R/S |
3.18 -09 |
Enter real-world C2 and compute R1 | 3.3 EEx CHS 9 R/S |
9.59 03 |
Enter real-world R1 and compute R2 | 10 EEx 3 R/S |
97.0 03 |
Enter real-world R2 | 100 EEx 3 R/S |
0.00 00 |
Now determine the predicted actual performance:
Description | Keystrokes | Display |
---|---|---|
Compute resulting f | E | 482. 00 |
Compute resulting Q | R/S | 1.96 00 |
Compute resulting H | R/S | 1.57 00 |
Notice that H is higher than the specified unity gain. This is because the filter is not possible to construct with unity gain when Q = 2. The smallest possible gain is H = 1.59 (which due to real-world components, has become Q = 1.89 and H = 1.57). To achieve H = 1, you will need either a pre-attenuator with low output impedance, or a post-attenuator with high input impedance.
Program Listing
Line | Instruction | Comments |
---|---|---|
01♦ | LBL “SK” | |
02♦ | LBL a | Enter and store f |
03 | STO 11 | |
04 | RTN | |
05♦ | LBL b | Enter and store Q |
06 | STO 12 | |
07 | RTN | |
08♦ | LBL c | Enter and store H (gain) |
09 | STO 13 | |
10 | RTN | |
11♦ | LBL d | Enter and store C (capacitor scale) |
12 | STO 00 | |
13 | RTN | |
14♦ | LBL e | Enter and store RF |
15 | STO 04 | |
16 | RTN | |
17♦ | LBL A | Low-pass filter: RG,C1,C2,R1,R2,R3 |
18 | CF 00 | |
19 | GTO 00 | |
20♦ | LBL C | High-pass filter: RG,C1,C2,R1,R2 |
21 | SF 00 | |
22♦ | LBL 00 | Forward solution |
23 | RCL 12 | |
24 | 2.2 | |
25 | × | |
26 | .9 | |
27 | − | |
28 | RCL 12 | |
29 | .2 | |
30 | + | |
31 | ÷ | (2.2Q-0.9)/(Q+0.2) |
32 | 1 | |
33 | x≤y? | |
34 | x↔y | |
35 | STO 15 | K = max(1,(2.2Q-0.9)/(Q+0.2)) |
36 | RCL 13 | |
37 | x>y? | |
38 | STO 15 | K = max(H,1,(2.2Q-0.9)/(Q+0.2)) |
39 | RCL 15 | |
40 | ÷ | H/K |
41 | x=0? | |
42 | 1 | Use α = 1 if H/K = 0 (because H was 0) |
43 | FS? 00 | |
44 | 1 | Always use α = 1 for a high-pass filter |
45 | STO 14 | α = H/K (always 1 for a high-pass filter or H = 0) |
46 | RCL 04 | |
47 | RCL 15 | |
48 | 1 | |
49 | − | |
50 | x≠0? | |
51 | ÷ | RG = RF/(K-1) if K ≠ 1, or zero if K = 1 |
52 | R/S | Display RG and let user change it |
53 | STO 05 | |
54 | .1 | Initialize n to √0.1; |
55 | √x | |
56 | XEQ 07 | n(1+√(1+4Q2(1+n2)(K-1)))/(2Q(1+n2)) |
57 | RCL 08 | √0.1 was stored here by subroutine 7 |
58 | x≤y? | |
59 | x↔y | max(n, n(1+√(1+4Q2(1+n2)(K-1)))/(2Q(1+n2))) |
60 | FS? 00 | High-pass filter? |
61 | XEQ 06 | 2nQ/(1+√(1+4Q2(K-1-n2))) |
62 | STO 08 | |
63 | RCL 00 | |
64 | RCL 08 | |
65 | ÷ | C1 = C/n |
66 | R/S | Display C1 and let user change it |
67 | STO 06 | |
68 | RCL 08 | |
69 | RCL 00 | |
70 | × | C2 = nC |
71 | R/S | Display C2 and let user change it |
72 | STO 07 | |
73 | RCL 06 | |
74 | × | |
75 | √x | √(C1C2) |
76 | XEQ 04 | Multiply by 2πf and take reciprocal |
77 | STO 09 | N = 1/(2πf√(C1C2)) |
78 | RCL 07 | |
79 | RCL 06 | |
80 | ÷ | |
81 | √x | n = √(C2/C1) |
82 | XEQ 03 | 2nQ/(1+√(1+4Q2(K-1-n2))) or n(1+√(1+4Q2(1+n2)(K-1)))/(2Q(1+n2)) |
83 | STO 08 | |
84 | RCL 09 | |
85 | × | |
86 | STO 03 | (R1||R3) = nN |
87 | RCL 14 | |
88 | ÷ | R1 = (R1||R3)/α |
89 | R/S | Display R1 and let user change it |
90 | STO 01 | |
91 | RCL 09 | |
92 | RCL 08 | |
93 | ÷ | R2 = N/n |
94 | R/S | Display R2 and let user change it |
95 | STO 02 | |
96 | RCL 03 | |
97 | 1 | |
98 | RCL 14 | |
99 | − | ( 1-α (R1||R3) ) |
100 | x≠0? | |
101 | ÷ | R3 = (R1||R3)/(1-α) if α ≠ 1, or zero otherwise |
102 | R/S | Display R3 and let user change it |
103 | STO 03 | |
104 | RTN | |
105♦ | LBL 04 | Multiply by 2πf and take reciprocal |
106 | RCL 11 | f |
107 | × | |
108♦ | LBL 01 | Multiply by 2π and take reciprocal |
109 | 2 | |
110 | × | |
111 | π | |
112 | × | |
113 | 1/x | |
114 | RTN | |
115♦ | LBL 03 | Compute either 2xQ/(1+√(1+4Q2(K-1-x2))) or x(1+√(1+4Q2(1+x2)(K-1)))/(2Q(1+x2)) |
116 | FS? 00 | High-pass filter? |
117 | GTO 07 | |
118♦ | LBL 06 | Subroutine to compute 2xQ/(1+√(1+4Q2(K-1-x2))) |
119 | STO 08 | Save x for later use |
120 | RCL 12 | |
121 | 2 | |
122 | × | ( 2Q x ) |
123 | × | ( 2xQ ) |
124 | LASTx | ( 2Q 2xQ ) |
125 | x2 | ( 4Q2 2xQ ) |
126 | RCL 15 | |
127 | 1 | |
128 | − | |
129 | RCL 08 | |
130 | x2 | |
131 | − | ( K-1-x2 4Q2 2xQ ) |
132 | × | ( 4Q2(K-1-x2) 2xQ ) |
133 | XEQ 09 | ( 1+√(1+4Q2(K-1-x2)) 2xQ ) |
134 | ÷ | |
135 | RTN | |
136♦ | LBL 07 | Subroutine to compute x(1+√(1+4Q2(1+x2)(K-1)))/(2Q(1+x2)) |
137 | STO 08 | Save x in register 8 for later use both by this subroutine and the caller |
138 | XEQ 08 | ( 2Q 1+x2 ) |
139 | x2 | |
140 | × | ( 4Q2(1+x2) ) |
141 | RCL 15 | |
142 | 1 | |
143 | − | ( K-1 4Q2(1+x2) ) |
144 | × | ( 4Q2(1+x2)(K-1) ) |
145 | XEQ 09 | ( 1+√(1+4Q2(1+x2)(K-1)) ) |
146 | RCL 08 | |
147 | × | ( x(1+√(1+4Q2(1+x2)(K-1))) ) |
148 | RCL 08 | |
149 | XEQ 08 | ( 2Q 1+x2 x(1+√(1+4Q2(1+x2)(K-1))) ) |
150 | × | ( 2Q(1+x2) x(1+√(1+4Q2(1+x2)(K-1))) ) |
151 | ÷ | |
152 | RTN | |
153♦ | LBL 08 | Subroutine to populate stack with 2Q 1+x2 |
154 | x2 | |
155 | 1 | |
156 | + | |
157 | RCL 12 | |
158 | 2 | |
159 | × | |
160 | RTN | |
161♦ | LBL 09 | Subroutine to compute 1+√(1+x) |
162 | 1 | |
163 | + | |
164 | √x | |
165 | 1 | |
166 | + | |
167 | RTN | |
168♦ | LBL E | Compute actual f, Q, and Gain |
169 | XEQ 02 | R1 or (R1||R3) |
170 | RCL 06 | |
171 | × | |
172 | STO 08 | Save R1C1 for use in calculating Q |
173 | RCL 02 | |
174 | RCL 07 | |
175 | × | |
176 | STO 09 | Save R2C2 for use in calculating Q |
177 | × | |
178 | √x | |
179 | XEQ 01 | Multiply by 2π and take reciprocal |
180 | STO 10 | Save actual f for use in calculating Q |
181 | R/S | Display actual f |
182 | RCL 09 | ( R2C2 ) |
183 | RCL 08 | ( R1C1 R2C2 ) |
184 | FS? 00 | High-pass filter? |
185 | x↔y | ( R2C2 R1C1 ) |
186 | 1 | |
187 | RCL 15 | |
188 | − | |
189 | × | |
190 | + | |
191 | XEQ 02 | R1 or (R1||R3) |
192 | RCL 07 | |
193 | × | |
194 | + | |
195 | RCL 10 | Recall actual frequency |
196 | × | |
197 | XEQ 01 | Multiply by 2π and take reciprocal |
198 | R/S | Display actual Q |
199 | RCL 04 | |
200 | RCL 05 | |
201 | x≠0? | |
202 | ÷ | |
203 | 1 | |
204 | + | |
205 | XEQ 02 | R1 or (R1||R3) |
206 | × | |
207 | RCL 01 | |
208 | ÷ | |
209 | RTN | Return actual Gain |
210♦ | LBL 02 | Return either R1 or (R1||R3) |
211 | RCL 01 | |
212 | FS? 00 | High-pass filter? |
213 | RTN | Return just R1 |
214 | 1/x | |
215 | RCL 03 | |
216 | x≠0? | R3 exists? (0 means not) |
217 | 1/x | |
218 | + | |
219 | 1/x | |
220 | RTN |
Registers and Flags
Register | Use |
---|---|
00 | C – capacitor scale |
01,02,03 | R1, R2, R3 – filter resistors |
04,05 | RF, RG – feedback resistors |
06,07 | C1, C2 – capacitors |
08 | n – variable used during computation |
09 | N – variable used during computation |
11 | f – cutoff frequency |
12 | Q – filter quality |
13 | H – overall gain |
14 | α – input attenuator gain |
15 | K – internal gain |
10 | Temporary register |
Flag | Meaning |
---|---|
00 | High-pass filter |
Revision History
2008-Dec-21 — Initial release.
References
1. Analysis of the Sallen-Key Architecture (Rev.B), Texas Instruments Application Report SLOA024B, James Karki, 2002
2. Low-Sensitivity, Lowpass Filter Design, National Semiconductor (now Texas Instruments) Application Note OA-27, Kumen Blake, 1996
3. Low-Sensitivity, Highpass Filter Design, National Semiconductor (now Texas Instruments) Application Note OA-29, Kumen Blake, 1996
Related Articles
If you've found this article useful, you may also be interested in:
- Op-Amp Gain and Offset Design with the HP-41C Programmable Calculator
- Op-Amp Oscillator Design with the HP-41C Programmable Calculator
- Low-Sensitivity Sallen-Key Filter Design with the HP-67 Programmable Calculator
- Op-Amp Gain and Offset Design with the HP-67 Programmable Calculator
- Op-Amp Oscillator Design with the HP-67 Programmable Calculator
- Resistor Network Solver for the HP-67 Programmable Calculator
- A Matrix Multi-Tool for the HP 35s Programmable Calculator
- Curve Fitting for the HP 35s Programmable Calculator
If you've found this article useful, consider leaving a donation in Stefan's memory to help support stefanv.com
Disclaimer: Although every effort has been made to ensure accuracy and reliability, the information on this web page is presented without warranty of any kind, and Stefan Vorkoetter assumes no liability for direct or consequential damages caused by its use. It is up to you, the reader, to determine the suitability of, and assume responsibility for, the use of this information. Links to Amazon.com merchandise are provided in association with Amazon.com. Links to eBay searches are provided in association with the eBay partner network.
Copyright: All materials on this web site, including the text, images, and mark-up, are Copyright © 2024 by Stefan Vorkoetter unless otherwise noted. All rights reserved. Unauthorized duplication prohibited. You may link to this site or pages within it, but you may not link directly to images on this site, and you may not copy any material from this site to another web site or other publication without express written permission. You may make copies for your own personal use.
'Angel Martin
October 25, 2015
Hi Stefan, first off many thanks for sharing your programs on your web site, and congrats of a superb documentation – real world class.
I’m also writing to ask you if you’ll agree with adding three of those into a EE-Filters collection that I’ve prepared into a ROM image – to use with v41 Emulator or MLDL-like devices (You heard of the 41CL perhaps?).
You’re probably moved to greener pastures but if you’d like to try the module let me know an email address where I can send it to – I modify them a litlee bit to take advantage of the 41-style data entry and menu prompts, but besides that they’re as you wrote them.
Hope to hear form you soon, thanks again and best wishes,
‘Angel Martin