Digitaler Filter für Mikrocontroller
Auf dieser Seite wird eine Form eines digitalen IIR-Filters beschreiben, welches mit reiner
Integer-Mathematik in einen Mikrocontroller implementiert werden
kann. Er arbeitet als Tiefpassfilter, welcher relativ einfach
auch als Hochpassfilter verwendet werden kann. Für einen Bandpass oder Bandsperre benötigte man 2 Filter in Reihe.
Der Vorteil ist, dass er sehr schnell rechnet und
wenig Code benötigt. Dadurch erreicht man eine hohe Abtastrate (= SPS oder Samples per second).
Der Nachteil ist, dass er nur in einem relativ
engen Verstellbereich zuverlässig läuft und je nach Einstellung einen hohen Klirrfaktor
aufweist. Den Nachteilen kann man aber begegnen, indem man ihn auf
64-Bit erweitert. Manche Compier bieten das, in C z.B. mit dem long
long int Datentyp (meist als int64_t definiert). Ansonsten müsste
man die Mathematik selber in Assembler programmieren (z.B. in einem 8-Bit Mikro wie der 8051).
Digital nachgebildetes RC-Filter (Tiefpass)
Der Kondensator wird vom Strom durch den
Widerstand geladen. Je kleiner die Spannungsdifferenz zwischen Eingang
und Ausgang, desto kleiner der Ladestrom durch den Widerstand bis zum
Ausgleich. Dies wird digital nachgebildet, indem in einem definierten
Zeitintervall die folgende Formel berechnet wird:
o = o(t-1) + k1·(i(t) – o(t-1))
|
|
Wobei:
o = Filter Ausgang (Out)
i = Filter Eingang (In)
k1 = Faktor für Filter Zeitkonstante (1-e-(t/(2*π*RC)))
t = Wert zum jetzigen Zeitpunkt (dieses Sample)
t-1 = Wert zum vorherigen Zeitpunkt (vorheriges Sample)
Somit ist z.B. i(t) der Eingangswert des jetzigen Intervalls und o(t-1) der Ausgangswert der vorherigen Intervalls.
Aktives Filter
Beim Aktiven Filter (bekannt als aktive Filter, meist mit
Operationsverstärker aufgebaut) wird der Ausgang rückgekoppelt so dass
die Ausgangsbewegung beschleunigt wird und ein schnelleres Einschwingen
des Endwertes erreicht wird. Dadurch verringert sich für eine bestimmte
Grenzfrequenz der Faktor k1, was wiederum die Filtersteilheit verbessert.
Die Formel sieht dann so aus:
o = o(t-1) + k1·(i(t) – o(t-1)) + k2·(o(t-1) – o(t-2))
Wobei:
o = Filter Ausgang
i = Filter Eingang
k1 = Faktor für Filter Zeitkonstante
k2 = Faktor für Filter Rückkoppelung, auch bekannt als Q-Faktor
Mit zunehmend erhöhtem k2 nimmt die Neigung zum Überschwingen zu.
Beispiel einer Sprungantwort mit unterschiedlichen k1-Werten, k2 = 0.
|
|
Beispiel einer Sprungantwort mit unterschiedlichen k2-Werten, k1 = 0.1.
|
|
Filter Algorithmus
Dieser kann in einer beliebigen Programmiersprache geschrieben werden mit Fließkomma-Mathematik.
Statische Variablen:
delta, out
Berechnung des Wertes, welcher zu out addiert werden muss (delta ), wird
separat berechnet, um eine bessere Auflösung zu erhalten wenn out einen
hohen Wert annimmt. Zudem entspricht dieser Wert beim nächsten Intervall
dem Term (Mathematischer Ausdruck) o(t-1) – o(t-2)
und kann wiederverwendet werden.
delta = (k1 * (in - out)) + (k2 * delta);
Danach den Wert addieren.
out = out + delta
Implementation mit 16/32-Bit Integer Mathematik in C
Damit die Berechnung wenig Zeit braucht, soll der Filter ausschließlich mit 16-Bit bzw. 32-Bit Mathematik ausgeführt werden. Die
Multiplikationen können somit nur als 16x16 Bit erfolgen, was ein
32-Bit Resultat ergibt. Da k1 und k2 immer < 1.0 sind, müssen diese
mit 216 multipliziert werden und dann werden vom 32-Bit Resultat nur
die signifikanten 16-Bit weiterverarbeitet, was wiederum einem Teiler
von 216 entspricht. Ebenso müssen die Eingang und Ausgangswerte
auf 216 skaliert werden.
Man kann sich das so vorstellen, dass die 16-Bit nicht eine Zahl von
0...65535 darstellen, sondern 0…0.99998 (65535 / 65536). Multipliziert
man z.B. 0.5 x 0.5, ergibt das 0.25. Auf 216 skaliert, ergibt das:
32768 x 32768 = 1073741824 / 216 = 16384, was 0.25 entspricht.
Das Eingangssignal kann auch negative Werte annehmen wie z.B. bei
einem Audiosignal. Entsprechend wird das Signal dann auf +/- 32767
skaliert. Das folgende Code-Beispiel in C ist auch entsprechend dafür
ausgelegt.
Die Makros mit den Konstanten k1 und k2 müssen bekannt sein z.B. aus der Tabelle weiter unten (Beispiel für Fg=3500Hz @ 44kSPS):
#define K1I (int32_t)11317
#define K2I (int32_t)33115
Union definieren um in der 32-Bit Variable auf die signifikanten 16-Bit zugreifen zu können:
union {
int32_t l;
int16_t i[2];
} liUnion;
Statische Variablen:
static liUnion Delta = {0}, Out = {0};
Temporäre Variablen:
int16_t iTemp, iIn, iOut;
iIn muss den Eingangswert für das Filter enthalten, skaliert auf +/-215.
Delta enthält den vorherigen Wert, welcher zu Out addiert wurde und entspricht somit dem Term (o(t-1) – o(t-2)).
Die 16 MSBit von Delta nach iTemp kopieren.
iTemp = Delta.i[1];
iTemp aufrunden, falls das höchste verworfene Bit 1 beträgt.
if(Delta.i[0] & 0x8000) iTemp++;
32-Bit Multiplikation des Terms k2·(o(t-1) – o(t-2))
Delta.l = (K2I * (int32_t)iTemp);
Kompletter Term k1·(i(t) – o(t-1)) + k2·(o(t-1) – o(t-2)). Out enthält vor der Addition von Delta weiter Unten den Wert o(t-1).
Delta.l += K1I * (int32_t)(iIn – Out.i[1]);
32-Bit Addition zu Out
Out.l += Delta.l;
Die 16 signifikanten Bits nach iOut kopieren
iOut = Out.i[1];
Damit weniger Probleme mit der Auflösung der Berechnungen entstehen, ist
es vorteilhaft, die Abtastrate so anzupassen, dass die
Grenzfrequenz im Bereich von 0.01….0.1 mal die Abtastrate liegt.
So kann sie z.B. für 44'000 SPS im Bereich 440...4400Hz liegen. Soll sie
höher oder tiefer sein, sollte die Abtastrate entsprechend
angepasst werden.
Sollen tiefere Grenzfrequenzen mit hoher Abtastrate gefiltert werden,
kann man mehrere Filter hintereinander laufen lassen, wobei der
nachfolgende Filter mit tieferer Abtastrate läuft. Der erste Filter
verlangsamt den Eingang des zweiten Filters so dass man nicht über das Nyquist-Shannon-Abtasttheorem stolpert.
Hochpass
Um aus einem Tiefpassfilter einen Hochpassfilter zu erhalten, kann
man einfach den Ausgang vom Eingang subtrahieren. Beispiel mit einem
Filter als Funktion Tiefpass(In) :
TPout = Tiefpass(In)
Entsprechend ist der Hochpass:
HPout = In – TPout
Benötigt man nur einen Hochpass:
HPout = In – Tiefpass(In)
Bestimmung von k1 und k2
Es gibt Formeln zur Bestimmung der Werte. Allerdings werden die
errechneten Werte mit steigender Grenzfrequenz ungenauer da weniger
Intervalle berechnet werden. Am einfachsten verwendet man die folgende
Tabelle:
K1 und K2 Werte sind bereits mit 65536 multipliziert.
Die Q-Werte definieren das maximale Überschwingen in der Sprungantwort. Je
höher der Q-Wert, desto steiler das Filter, aber auch desto mehr
überschwingt es. Es ist vergleichbar mit den Filterberechnungen nach
Bessel (ca. 0.1%), Butterworth (ca. 5%) und Tschebyscheff (ca. 20%).
Für Audioanwendungen ist 5% - 10% am Besten, für Messanwendungen 0.1%...1%.
Die Grenzfrequenz ist für die Intervallzeit angegeben (also 1/SPS). Beispiel:
• SPS = 44000, Fg = 3500Hz, Q=5%: 3500/44000 =0.0795
Tabelle Fg/SPS 0.0794, 5% K1I=11317, K2I=33115
• SPS = 100, Fg = 3.5Hz, Q=15%: 3.5/100 =0.035
Tabelle Fg/SPS 0.0355, 15% K1I=1647, K2I=55221
|