Raspberry Pi Pico Lightcontroller, 3-Kanal Lichtorgel und Beat Lauflicht

  Home     Übersicht     Hardware     Software     Filtertechnik  

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


© 2023-2024 by Stefan Ludescher