1 1 Einleitung
Tiefpass-Filter-Projekt
Beispielprojekt für die Verwendung der
Mikro-Controller-Plattform
mit Hilfe der initDevice-Klasse
Ort: Hochschule Karlsruhe - Technik und Wirtschaft
Fakultät für Elektro- und Informationstechnik
Name: Florian Kanis
Zeitraum: Wintersemester 2015/16
Inhaltsverzeichnis
1 Einleitung .............................................................................................................. 3
2 Implementierungsablauf ........................................................................................ 4
3 Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung ...................... 5
3.1 Konfiguration von Timer2 .............................................................................................. 5
3.2 Konfiguration von ADC1 ............................................................................................... 6
3.3 Implementierung der ADC-Interrupt-Service-Routine ................................................... 6
3.4 Test der ADC-Interrupt-Service-Routine........................................................................ 7
3.5 Konfiguration von DAC1 ............................................................................................... 7
3.6 Konfiguration der Zusatzbeschaltung von ADC1 und DAC1 ........................................ 8
3.7 Test mit Signalgenerator und Oszilloskop ...................................................................... 8
4 Implementierung der digitalen Tiefpass-Filter .................................................. 9
4.1 Rekursives Tiefpass-Filter ............................................................................................ 10
4.2 Moving-Average-Filter ................................................................................................. 10
4.3 Test der Filter mit Signalgenerator und Oszilloskop .................................................... 11
5 UART-Kommunikation zwischen Mikro-Controller-Plattform und PC ........... 13
5.1 Konfiguration der USART1-Schnittstelle ..................................................................... 13
5.2 Implementierung der USART1-Interrupt-Service-Routine .......................................... 14
5.3 Definition der Funktion USART_SEND() ................................................................... 15
5.4 Test der USART-Interrupt-Service-Routine ................................................................. 15
5.5 Implementierung der Filtersteuerung ............................................................................ 16
5.6 Test der UART-Kommunikation mit Hyperterminal .................................................... 17
5.7 Test der Filter mit Hilfe der MATLAB GUI ................................................................ 18
6 Abbildungsverzeichnis ..................................................................................... 21
7 Anhang 1: Quellcode ........................................................................................ 22
Einleitung 3
1 Einleitung
In diesem Dokument wird ein Beispielprojekt für die Verwendung der Mikro-Controller-
Plattform mit Hilfe der initDevice-Klasse beschrieben. Die beschriebene Vorgehensweise ba-
siert auf dem Dokument ´Mesysto Mikro-Controller-Plattform.pdf´, auf welches sich in diesem
Dokument mit [1] bezogen wird. Auf die verwendeten Funktionen der initDevice-Klasse in der
Datei initDevice.cpp wird sich mit [2] bezogen.
Implementiert wurde ein Programm, welches ein analoges Signal einliest, dieses digitalisiert,
digital filtert und zum Schluss wieder in ein analoges Signal wandelt. Es kann zwischen einem
rekursiven Tiefpass-Filter und einem Moving-Average-Filter, jeweils mit anpassbaren Filterko-
effizienten, gewählt werden. Die Steuerung der Filter, also die Auswahl des Filters und die An-
passung der Filterkoeffizienten, kann entweder mit Hilfe eines Terminalprogramms oder der
bereitgestellten MATLAB GUI durchgeführt werden. Es ist zu beachten dass sie Verwendung
der MATLAB GUI nur in Kombination mit der Messkarte BNC-2110 von National Instruments
durchgeführt werden kann. Über diese Messkarte werden die benötigten Signale von der MAT-
LAB GUI an die Mikro-Controller-Plattform übergeben und die Ausgabewerte wiederum einge-
lesen, um diese dann in der MATLAB GUI darzustellen.
Dieses Dokument ist so aufgebaut, dass die Reihenfolge der diskutierten Themenbereiche den
zeitlichen Ablauf der Implementierung dieses Programms wiederspiegelt.
In Kapitel 2 wird zu Beginn dieser generelle Ablauf besprochen.
Kapitel 3 diskutiert die Schritte, die benötigt werden, um ein Eingangssignal zu digitalisieren,
das digitale Signal für die Signalverarbeitung bereitzustellen und es schließlich wieder in ein
analoges Signal umzuwandeln. Dabei werden die einzelnen Implementierungsschritte sowie
deren Testmöglichkeiten in der Reihenfolge diskutiert, in der sie implementiert wurden.
In Kapitel 4 wird daraufhin auf die Implementierung der beiden digitalen Filter und die Mög-
lichkeit, deren Verhalten zu testen, eingegangen. Bis zu diesem Punkt kann dieses Dokument
als Grundlage für jegliche Signalverarbeitung mit digitalen Filtern verwendet werden.
In Kapitel 5 wird auf die Kommunikation zwischen PC und Mikro-Controller-Plattform mittels
Universal Asynchronous Synchronous Receive Transmit-Schnittstelle (USART) eingegangen.
Zu Beginn wird diese generell diskutiert und im späteren Verlauf des Kapitels speziell auf die
USART-Kommunikation zum Test der Implementierung der Filtersteuerung eingegangen. Für
andere Projekte, die eine USART-Kommunikation benötigen, ist Kapitel 5 daher auch von Inte-
resse.
Kapitel 6 enthält den kompletten implementierten Quellcode und kann als Referenz verwendet
werden, da in den vorigen Kapiteln nur die grundlegenden Funktionalitäten besprochen werden.
4 Implementierungsablauf
2 Implementierungsablauf
In diesem Kapitel wird der generelle Ablauf der Implementierung beschrieben und ist in Abbil-
dung 1 dargestellt. In den folgenden Kapiteln wird daraufhin auf die einzelnen Schritte detail-
liert eingegangen. Auf den ersten Schritt, dem Erstellen des Projektes, wird dabei verzichtet, da
[1] hierfür alle notwendigen Information bereitstellt.
Der erste Arbeitsschritt, der in diesem Dokument diskutiert wird, ist daher die Konfiguration der
Peripherie, um ein analoges Signal zu digitalisieren (ADC1), dieses zwischen zu speichern und
dann wieder in ein analoges Signal umzuwandeln (DAC1). Die Abtastfrequenz der AD-
Wandlung wird hierbei mit Timer2 gesteuert. Des Weiteren muss die Zusatzbeschaltung der
Mikro-Controller-Plattform konfiguriert werden.
Der nächste Schritt ist die Implementierung der digitalen Tiefpass-Filter mittels Differenzen-
gleichung. Er wird in Kapitel 4 diskutiert. Hier wird die Implementierung eines rekursiver Tief-
pass-Filters und eines Moving-Average-Filters besprochen.
Zum Schluss wird in Kapitel 5 die Konfiguration der USART1-Schnittstelle diskutiert. Diese
wird verwendet, um die Filtersteuerung, also die Auswahl des Filters sowie die Festlegung der
jeweiligen Filtereigenschaften, von einem Terminalprogramm oder einer MATLAB GUI aus zu
steuern. Die Kommunikation via USART wird außerdem verwendet, um Messwerte von der
Mikro-Controller-Plattform an die MATLAB GUI zu senden.
Abbildung 1: Reihenfolge der Implementierung
Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung 5
3 Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschal-
tung
In diesem Kapitel wird beschrieben, wie das analoge Eingangssignal mit Hilfe des AD-
Wandlers (ADC1) digitalisiert wird, die Abtastfrequenz mit Timer2 festgelegt wird und das
digitale Signal wieder mit Hilfe des DA-Wandlers (DAC1) wieder in ein analoges Signal ge-
wandelt wird. Des Weiteren wurde die Zusatzbeschaltung der Mikro-Controller-Plattform für
ADC1 und DAC1 konfiguriert. Die Reihenfolge dieses Prozesses, sowie die Möglichkeit den
jeweiligen Implementierungsstatus zu testen, ist in Abbildung 2 dargestellt
Abbildung 2:Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung
3.1 Konfiguration von Timer2
Die Abtastfrequenz von ADC1 wurde, entsprechend Kapitel 2.2 in [1], durch die Initialisierung
von Timer2 mit folgendem Code durchgeführt:
device.setTimer(2);
device.setfTimer(22050);
device.setTimerOutput(Trigger);
device.timerInit();
Mit der Funktion setTimer(2) wird zu Beginn Timer2 ausgewählt. fTimer() stellt daraufhin die
Abtastfrequenz auf 44,1kHz ein. Hierbei ist zu beachten, dass wenn Timer2 als Trigger für
ADC1 verwendet wird, die Funktion setfTimer() nicht auf 44100 sondern auf die Hälfte, also
22050, eingestellt werden muss. Der Faktor 0,5 ist zu verwenden, wenn der Timer Output als
6 Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung
Trigger für eine andere Peripherie verwendet wird. setTimerOutput(Trigger) definiert daraufhin,
dass das ausgelöste Event von Timer2 als Trigger für eine andere Peripherieeinheit verwendet
wird. Die Verknüpfung von Timer2 und ADC1 wird im kommenden Kapitel 3.2 beschrieben.
Zum Schluss werden die Einstellungen mit der Funktion timerInit() übernommen. Die von [2]
bereitgestellten Funktionen setTimer(), setfTimer(), setTimerOutput() und timerInit() konfigurie-
ren im Hintergrund automatisch alle relevanten Elemente. Für genauere Informationen zu den
verwendeten Funktionen sind in [2] die Funktionsdefinitionen einzusehen.
3.2 Konfiguration von ADC1
Die Konfiguration des ADC1 wurde, wie in Kapitel 2 in [1] beschrieben, mit folgendem Code
durchgeführt:
device.setAdc(1);
device.setADCTrigger(timer2ADC_TRGO);
device.adcInit();
device.setIRQChannel(adcInterrupt);
device.nvicInit();
Zu Beginn wurde mit der Funktion setAdc(1) die Verwendung von ADC1 definiert. Daraufhin
wurde mit setADCTrigger(timer2ADC_TRGO) festgelegt, dass der Ausgang von Timer2 als
Trigger für ADC1 verwendet wird. Mit der Funktion adcInit() werden diese Einstellungen über-
nommen. Da mit jedem neu gewandelten Wert ein Interrupt ausgelöst wird, wird im Nested
Vector Interrupt Controller (NVIC) der Kanal für den ADC-Interrupt freigegeben. Das wird mit
setIRQChannel(adcInterrupt) bewerkstelligt, woraufhin die Einstellung mit der Funktion nvi-
cInit() übernommen wird. Die von [2] bereitgestellten Funktionen setAdc(), setADCTrigger(),
adcInit(), setIRQChannel() und nvicInit() konfigurieren im Hintergrund automatisch alle rele-
vanten Elemente (GPIOs, Prescaler, Triggereigenschaften, usw.). Für genauere Informationen
zu den verwendeten Funktionen sind in [2] die Funktionsdefinitionen einzusehen.
3.3 Implementierung der ADC-Interrupt-Service-Routine
Die ADC-Interrupt-Service-Routine wird aufgerufen, sobald ADC1 einen neuen Wert gewan-
delt hat und dieser zur Verfügung steht. Dies wird durch das End-Of-Conversion-Flag (EOC)
anzeigt. Abgesehen von dem regelmäßigen Aufrufen im laufenden Betrieb wird die ADC-
Interrupt-Service-Routine einmal zu Beginn, bei der Ausführung des Befehls adcInit(), aufgeru-
fen. Die ADC-Interrupt-Service-Routine wurde wie in Kapitel 2 in [1] beschrieben, mit folgen-
dem Code implementiert:
extern "C" void ADC_IRQHandler(void)
{
If(ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET)
{
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
//hier folgt später der weitere Code
//siehe Kapitel 3.5 Konfiguration von DAC1
}
}
Zu Beginn wird abgefragt, ob das EOC-Flag gesetzt ist. Ist das der Fall, so wird es zurückge-
setzt, sodass nach der nächsten Wandlung die ADC-Interrupt-Service-Routine erneut aufgerufen
werden kann. An dieser Stelle wird später die Signalverarbeitung mit den digitalen Filtern und
die Ausgabe über den DAC implementiert. Die Funktionen ADC_GetITStatus() und
Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung 7
ADC_ClearITPendingBit() werden von der STM-Bibliothek, siehe Kapitel 1.1 in [1] bereitge-
stellt und wurden nicht eigens in [2] definiert.
3.4 Test der ADC-Interrupt-Service-Routine
Wenn Timer2 und ADC1 fertig initialisiert sind, muss getestet werden, ob das Programm im
laufenden Betrieb wie gewünscht in die ADC-Interrupt-Service-Routine springt. Für einen ers-
ten Test wird daher der Debugger von Keil uVision genutzt. Führen Sie folgende Schritte nach-
einander aus:
Klicken Sie auf ‘F7‘, um den geschriebenen Code in die Target-Files umzusetzen
Klicken Sie auf , um den Debugger zu starten
Setzten Sie einen Breakpoint in die ADC-Interrupt-Service-Routine
Klicken Sie auf , um den Programmcode vom Debugger in den Kontroller zu laden
Klicken Sie auf , um das Programm zu starten
Das Programm sollte nun bei dem Breakpoint in der ADC-Interrupt-Service-Routine stoppen.
Falls das Programm nicht an dieser Stelle stoppt, muss die Initialisierung von Timer2 und
ADC1 sowie die Definition der ADC-Interrupt-Service-Routine auf Fehler untersucht werden.
3.5 Konfiguration von DAC1
Mit dem folgenden Code wurde, entsprechend Kapitel 3 in [1], der DAC1 konfiguriert:
device1.setDac(1);
device1.setDacTrigger(None);
device1.dacInit();
Analog zu der Initialisierung des ADC1 wurde DAC1 mit der Funktion setDac(1) ausgewählt.
setDacTrigger(None) definiert daraufhin, dass der DAC nicht durch einen externen Trigger
gesteuert wird. Bei dieser Konfiguration werden Daten ausgegeben, sobald die Ausgabe per
Funktionsaufruf gestartet wird und nicht per externem Trigger. Diese Initialisierung wurde
schließlich wieder mit der Funktion dacInit() übernommen. Die von [2] bereitgestellten Funkti-
onen setDac(), setDacTrigger() und dacInit() konfigurieren im Hintergrund automatisch alle
relevanten Elemente (GPIOs, Prescaler, Triggereigenschaften, usw.). Für genauere Informatio-
nen zu den verwendeten Funktionen sind in [2] die Funktionsdefinitionen einzusehen.
Das Auslesen der digitalisierten Werte des ADC1 und die Ausgabe dieser Werte mit dem DAC1
wurden mit folgendem Code in der ADC-Interrupt-Service-Routine implementiert:
convValue = ADC_GetVonversionValue(ADC1);
DAC_SetChannel1Data(DAC_Align_12b_R, convValue);
Der vom ADC1 bereitgestellte digitale Wert wird mit der Funktion ADC_GetConversionValue()
erfasst, in der Variablen convValue zwischengespeichert und dann mit der Funktion
DAC_SetChannel1Data() über den DAC1 wieder ausgegeben. Diese beiden Funktionen werden
von der STM-Bibliothek, siehe Kapitel 1.1 in [1], bereitgestellt und wurden nicht eigens in [2]
definiert. Der Parameter DAC_Align_12b_R definiert, dass eine 12 Bit-Variable mit linksbündi-
ger Datenausrichtung an den DAC1 übergeben wird.
8 Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung
3.6 Konfiguration der Zusatzbeschaltung von ADC1 und DAC1
Die Konfiguration der Zusatzbeschaltung wurde, entsprechend Kapitel 8 in [1], mit folgendem
Code implementiert:
device1.setADCInputPath(1,Bypass);
device1.setDACOutputPath(1,Bypass);
Mit der Funktion setADCInputPath() wird der jeweilige ADC-Eingangspfad ausgewählt, in
diesem Fall der Pfad 1 und als Bypass konfiguriert. Analog dazu wurde der DAC-Ausgangspfad
von DAC1 konfiguriert. Diese von [2] bereitgestellten Funktionen konfigurieren im Hintergrund
die Multiplexer auf dem analogen Frontend der Mikro-Controller-Plattform. Für genauere In-
formationen zu den verwendeten Funktionen sind in [2] die Funktionsdefinitionen einzusehen. Für detaillierte Informationen zu der verwendeten Hardware wird auf Kapitel 8 in [1] verwie-
sen.
3.7 Test mit Signalgenerator und Oszilloskop
Mit der aktuellen Konfiguration wird ein Signal zwischen 0V und 3V vom ADC1 eingelesen, in
der ADC-Interrupt-Service-Routine zwischengespeichert und daraufhin vom DAC1 wieder
ausgegeben. Das Ausgangssignal deckt dabei ebenfalls einen Spannungsbereich von 0V bis 3V
ab. Um diese Konfiguration zu testen, wurden ein Signalgenerator und ein Oszilloskop verwen-
det. Dabei wurden folgenden Werte am Signalgenerator eingestellt:
Frequenz 1kHz
Amplitude 100mV
Offset 750mV
Der Ausgang des Signalgenerators wurde mit einem T-Stücks ausgesplittet und einmal direkt
auf das Oszilloskop und einmal auf den Eingang des ADC1 der Mikro-Controller-Plattform
gegeben. Des Weiteren wurde der Ausgang DAC1 mit dem Oszilloskop verbunden. Somit
konnte das digital bearbeitete Signal mit dem Originalsignal verglichen werden.
Da die Implementierung auf der Mikro-Controller-Plattform das Signal lediglich abtastet und
wieder ausgibt, muss das Ausgangssignal der diskretisierten Form des Eingangssignals entspre-
chen. Bei genauer Betrachtung ist ein geringer Zeitverzug des diskretisierten Signals im Ver-
gleich zum Originalsignal zu erkennen. Dieser Zeitverzug ist der Verarbeitungszeit der AD-
Wandlung, der internen Speicherzugriffe und der abschließenden DA-Wandlung zu schulden.
Falls, wie in Abbildung 2 dargestellt, dieser Test nicht erfolgreich verlaufen ist, muss die Kon-
figuration von DAC und Zusatzbeschaltung, sowie die Funktionsaufrufe in der ADC-Interrupt-
Service-Routine überprüft werden.
Implementierung der digitalen Tiefpass-Filter 9
4 Implementierung der digitalen Tiefpass-Filter
In diesem Kapitel wird die Implementierung der digitalen Tiefpass-Filter diskutiert. Dabei wird,
wie in Abbildung 3 dargestellt, zuerst auf die Implementierung des rekursiven Tiefpass-Filters
und des Moving-Average-Filters in Kapitel 4.1 und 4.2 eingegangen. In Kapitel 4.3 wird dann
eine Möglichkeit für das Testen des Filterverhaltens präsentiert. An dieser Stelle ist zu erwäh-
nen, dass lediglich einer der beiden Filter zu einem Zeitpunkt verwendet werden kann, der je-
weils andere Filter muss in diesem Moment auskommentiert sein. In Kapitel 5.5 wird dann be-
schrieben, wie beide Filter verwendet werden können und per USART-Kommunikation der zu
verwendende Filter ausgewählt werden kann.
Abbildung 3: Implementierung der digitalen Tiefpass-Filter
Generell kann die Implementierung eines digitalen Filters mit einer Differenzengleichung und
einem Flussdiagramm dargestellt werden. Die Differenzengleichung beschreibt, wie sich der
aktuelle Ausgangswert aus gewichteten Werten vorhergegangener Eingangs- und Ausgangswer-
te zusammensetzt. Das rekursive Tiefpass-Filter und das Moving-Average-Filter werden in Ka-
pitel 4.1 und 4.2 anhand von ihrer Differenzengleichung charakterisiert und diskutiert. Das
Flussdiagramm ist generell für Filterimplementierungen gleich und ist in Abbildung 4 darge-
stellt.
Abbildung 4: Flussidagramm einer Filterimplementierung
Zu Beginn werden die Variablen, die den aktuellen Eingangswert sowie vorhergegangene Ein-
gangs- und Ausgangswerte speichern, initialisiert. Im zweiten Schritt wird die, für den jeweili-
10 Implementierung der digitalen Tiefpass-Filter
gen Filter repräsentative, Differenzengleichung berechnet und somit der aktuelle Ausgangswert
bestimmt. In Schritt drei und vier wird eine Speicherverwaltung durchgeführt, bei der die zu
Beginn initialisierten Variablen mit den aktuellen Werten beschrieben werden und der aktuelle
Ausgangswert ausgegeben wird. Dieser Prozess der Berechnung der Differenzengleichung, der
Speicherverwaltung und der Ausgabe der Ausgangsgröße wiederholt sich im laufenden Betrieb
zyklisch. Mit welchem Takt diese zyklische Verarbeitung durchgeführt wird, hängt von der
Position des zugehörigen Codes ab. In diesem Bespiel findet die Verarbeitung in der ADC-
Interrupt-Service-Routine statt und wird also 44100 Mal pro Sekunde ausgeführt.
4.1 Rekursives Tiefpass-Filter
Das implementierte rekursive Tiefpass-Filter kann mit Formel 1 beschrieben werden:
y k 1 GF u k GF y k 1
(1)
Der aktuelle Ausgangswert y[k] wird dabei aus dem aktuellen Eingangswert u[k] und dem vor-
hergegangenen Ausgangswert y[k-1] berechnet, wobei u[k] und y[k-1] verschieden gewichtet
werden können. Die Gewichtung dieser beiden Faktoren wird mit dem Gewichtungsfaktor GF
bestimmt, der Werte zwischen 0 und 1 annehmen kann.
Wenn GF = 0 gewählt wird, bedeutet dies, dass der aktuellen Eingangswert u[k] mit 1 gewichtet
wird und der vorhergegangene Ausgangswert y[k-1] mit 0. y[k] entspricht also u[k], was bedeu-
tet, dass die Zeitkonstante gleich null ist. Wenn GF = 1 gewählt wird, wird u[k] mit 0 gewichtet
und y[k-1] mit 1. In diesem Fall hat der aktuelle Eingangswert keinen Einfluss und das Aus-
gangssignal bleibt für immer auf dem Initialwert, der für gewöhnlich null ist. Die Zeitkonstante
ist daher unendlich. Werte zwischen 0 und 1 der Variablen GF definieren also die Zeitkonstante
des Filters, welche mit steigendem GF ebenfalls steigt.
Die Implementierung, deren generelle Struktur in Abbildung 4 dargestellt ist, wurde mit folgen-
dem Code durchgeführt:
// Variablen initialisieren (ADC-Wert lesen)
u = ADC_GetVonversionValue(ADC1);
// Differenzengleichung berechnen
y = (int16_t)((1-GF)*u+GF*yOld);
// Speicherverwaltung
yOld = y;
// Ausgabe der Ausgangsgröße
DAC_SetChannel1Data(DAC_Align_12b_R, y);
4.2 Moving-Average-Filter
Das Moving-Average-Filter wurde mit Formel 2 beschrieben:
15
nn 0
1y k d u k n
16
(2)
Der aktuelle Ausgangswert y[k] setzt sich dabei aus dem aktuellen und den vorhergegangenen
Eingangswerten u[k-n] zusammen. Da die Summe für einen Wertebereich von n = 0 bis n = 15
berechnet wird, wird der aktuelle Wert (n = 0) und die 15 vorherigen Eingangswerte (n = 1..15)
Implementierung der digitalen Tiefpass-Filter 11
berücksichtigt. Um jeden dieser 16 Werte gleich stark zu gewichten, wird das Ergebnis der
Summe schließlich durch 16 geteilt. Im Gegensatz zum rekursiven Tiefpass-Filter wird beim
Moving-Average-Filter lediglich der aktuelle und vergangene Eingangswert für die Berechnung
der Differenzengleichung berücksichtigt.
Die Implementierung, deren generelle Struktur in Abbildung 4 dargestellt ist, wurde mit folgen-
dem Code durchgeführt:
// Variablen initialisieren (ADC-Wert lesen)
u = ADC_GetVonversionValue(ADC1);
// Differenzengleichung berechnen
Ue[0]=u;
sum=0;
for(int i=0;i<M;i++){
sum = sum+(int)Ue[i];
}
y = (int16_t)(1/(float)M*sum);
// Speicherverwaltung
for(int i=M-1;i>0;i--){
Ue[i]=Ue[i-1];
}
// Ausgabe der Ausgangsgröße
DAC_SetChannel1Data(DAC_Align_12b_R, y);
4.3 Test der Filter mit Signalgenerator und Oszilloskop
Der Test der beiden Tiefpass-Filter ist ähnlich aufgebaut wie der in Kapitel 3.7 beschriebene
Test. Der jeweils nicht zu testende Filter muss dazu auskommentiert sein. Im Signalgenerator
wird ein Signal, das symmetrisch um 1,5V schwingt und eine Amplitude kleiner gleich 1,5V
hat, erzeugt und zusammen mit dem Ausgangssignal der Mikro-Controller-Plattform auf dem
Oszilloskop betrachtet und verglichen. Um das Frequenzverhalten des Filters darzustellen, gibt
es zwei Möglichkeiten. In beiden Fällen wird im Signalgenerator die Frequenz des erzeugten
Signals mit einem Sweep zwischen 1Hz und 20kHz variiert. Im Signalgenerator sind folgende
Einstellungen vorzunehmen:
Sine:
o Amplitude 100mV
o Offset 750mV
Sweep:
o Start 1Hz
o Stop 20kHz
o Sweep Time 10s
Der Ausgang des Signalgenerators wird mit einem T-Stück aufgesplittet und auf den ADC1–
Anschluss der Mikro-Controller-Plattform sowie auf das Oszilloskop gegeben. Der Ausgang des
DAC1 der Mikro-Controller-Plattform wird ebenfalls mit dem Oszilloskop verbunden, um Ein-
gangs- und Ausgangssignal des Filters miteinander zu vergleichen.
Die erste Möglichkeit besteht nun darin, das Zeitverhalten der beiden Signale miteinander zu
vergleichen. Dafür werden am Oszilloskop folgende Einstellungen vorgenommen:
Channel 1&2:
o Time/Division 1s/ -> Es können 10 Sekunden angezeigt werden
12 Implementierung der digitalen Tiefpass-Filter
o Voltage/Division 50mV/
o Kopplung AC -> Der Offset von 1,5V wird entfernt
Wenn nun Kanal 1 und 2 übereinander gelegt werden, wird dargestellt, wie das Filter das Ein-
gangssignal im Frequenzbereich von 1Hz bis 20kHz verändert. Da die Sweep Time auf 10s
eingestellt und auf dem Oszilloskop 1s/Division eingestellt wurde, wird jeweils der komplette
Sweep dargestellt. Innerhalb einer Division wird die Frequenz also um
(20000 - 1)/10Hz = 1999,9Hz ≈ 2kHz erhöht. Für die Implementierung des rekursiven Tiefpass-
Filters und des Moving-Average-Filters ergibt sich das Frequenzverhalten in Abbildung 5.
Abbildung 5: Darstellung des Frequenzverhalten im Zeitbereich
Auf der linken Seite von Abbildung 5 ist das rekursive Tiefpass-Filter mit einem Gewichtungs-
faktor von GF = 0.98 dargestellt. Wie erwartet entspricht das Verhalten dem eines Tiefpasses.
Auf der rechten Seite ist das Ausgangssignal des Moving-Average-Filters dargestellt mit einer
Länge von M = 16. Das sinx/x-Verhalten (Rechteck im Zeitbereich) ist deutlich zu erkennen.
Die zweite Möglichkeit besteht darin, mit einer FFT das Spektrum des Ausgangssignals darzu-
stellen. Dafür werden am Oszilloskop folgende Einstellungen vorgenommen:
Spanne 20kHz
Mitte 9kHz
Fenster Rechteck
Die Verschiebung um 1kHz (Mitte = 9kHz) wurde durchgeführt, um das Verhalten um eine
Frequenz von 0Hz besser beurteilen zu können. Das Ergebnis der FFT für das Ausgangssignal
des rekursiven Tiefpasses und des Moving-Average-Filters ist in Abbildung 6 dargestellt.
Abbildung 6: Darstellung des Frequenzverhaltens mittels FFT
Es ist zu sehen, dass das Ergebnis der FFT (pink) in Abbildung 6 der logarithmischen Darstel-
lung des Ergebnisses des Zeitverhaltens (grün) in Abbildung 5 entspricht.
UART-Kommunikation zwischen Mikro-Controller-Plattform und PC 13
5 UART-Kommunikation zwischen Mikro-Controller-Plattform
und PC
Auf dem zugrunde liegenden Mikro-Controller STM32F4 ist eine USART-Schnittstelle verfüg-
bar. Im Rahmen der verwendeten Mikro-Controller-Plattform und der verwendeten initDevice
Klasse wird diese Schnittstelle als Universal Asynchronous Receive Transmit (UART) verwen-
det. Das bedeutet, dass lediglich ein Kabel für das Empfangen und ein Kabel für das Versenden
zur Verfügung steht, jedoch kein Kabel für die Übertragung des Clock-Singals. Sie ermöglicht
die Kommunikation zwischen der Mikro-Controller-Plattform und dem PC.
Somit kann zum Beispiel die Filtersteuerung, also die Auswahl des Filters und die Anpassung
der Koeffizienten, vom PC durchgeführt werden und umkehrt Abtastwerte an den PC gesendet
werden. Der generelle Ablauf der Konfiguration der DAC1-Schnittstelle ist in Abbildung 7
dargestellt und wird in den folgenden Kapiteln im Detail beschrieben.
Abbildung 7: Konfiguration und Test der USART1-Schnittstelle
5.1 Konfiguration der USART1-Schnittstelle
Die Konfiguration wurde, entsprechend Kapitel 7.2 in [1] mit folgendem Code durchgeführt:
device1.setUsartChannel(1);
device1.setUsartDmaConnection(usartDmaOff);
device1.usartInit();
device1.setIRQChannel(usartInterrupt);
device1.nvicInit();
14 UART-Kommunikation zwischen Mikro-Controller-Plattform und PC
Die Verwendung der DAC1- Schnittstelle wird mit der Funktion setUsartChannel(1) definiert.
Mit der Funktion setUsartDmaConnection(usartDmaOff) wird daraufhin definiert, dass die Pe-
ripherie Direct Memory Access (DMA) nicht verwendet wird. Die DMA wird verwendet, wenn
beabsichtigt wird, den Datentransfer zwischen verschiedenen Peripherien untereinander, zwi-
schen einer Peripherie und dem Speicher oder speicherintern zu optimieren. Bei Verwendung
der DMA wird der Prozessor erheblich weniger belastet. Da dieses Programm recht kurz ist,
wurde diese Optimierung der Laufzeit nicht benötigt und die DMA nicht verwendet. Weitere
Informationen zur DMA sind in Kapitel 9 in [1] verfügbar. Mit dem Aufruf der Funktion usar-
tInit() werden schließlich die Einstellungen für die USART-Kommunikation übernommen.
Da die USART-Kommunikation mit Interrupts gesteuert wird, muss im NVIC der Kanal für den
USART1-Interrupt freigegeben werden. Das wird mit der Funktion setIRQChan-
nel(usartInterrupt) bewerkstelligt, woraufhin die Einstellung mit der Funktion nvicInit() über-
nommen wird.
Die von [2] bereitgestellten Funktionen setUsartChannel(), setUsartDmaConnection(), usar-
tInit(), setIRQChannel() und nvicInit() konfigurieren im Hintergrund automatisch alle relevan-
ten Elemente (GPIOs, Prescaler, USART-Einstellungen, usw.). Für genauere Informationen zu
den verwendeten Funktionen sind in [2] die Funktionsdefinitionen einzusehen. Die Einstellun-
gen, die mit diesem Code festgelegt wurden, sind in Tabelle 2 in [1] aufgelistet und werden an
dieser Stelle kurz wiederholt:
Übertragungsrate /Bit pro sek. 57600
Datenbits 8
Paritätsbit keine
Stoppbits 2
Flusssteuerung keine
Diese Werte werden in Kapitel 5.6 wieder benötigt. Dort wird die Kommunikation mit Hilfe des
Terminalprogramms Hyperterminal getestet wird.
5.2 Implementierung der USART1-Interrupt-Service-Routine
Die USART-Interrupt-Service-Routine wird aufgerufen, sobald ein UART-Interrupt ausgelöst
wird, z.B. im Falle eines neu empfangenen Datenwortes. Abgesehen von den Aufrufen im lau-
fenden Betrieb wird die USART1-Interrupt-Service-Routine einmal zu Beginn, bei der Ausfüh-
rung des Befehls usartInit(), aufgerufen.
Die USART1-Interrupt-Service-Routine wurde, angelehnt an Kapitel 7.2 in [1], mit folgendem
Code implementiert:
extern "C" void USART1_IRQHandler(void)
{
uint8_t signalFromGUI;
if(readyToSend){
ADC_Cmd(ADC1,DISABLE);
USART_SEND(char);
ADC_Cmd(ADC1,ENABLE);
readyToSend=0;
}
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
signalFromGUI = (USART_ReceiveData(USART1) & 0x7F);
}
}
UART-Kommunikation zwischen Mikro-Controller-Plattform und PC 15
Zu Beginn wird eine 8-Bit Variable definiert, in der im späteren Verlauf der empfangene Wert
gespeichert wird. Falls das Programm beabsichtigt, einen Wert via USART zu senden, wird
zuvor die Variable readyToSend gesetzt und der Code in der if-Abfrage ausgeführt. Zu Beginn
wird für den Zeitraum, in dem der Sendeprozess stattfindet, der ADC1 mit dem Befehl
ADC_Cmd(ADC1,DISABLE) deaktiviert. Das Senden der 8-Bit Daten wird mit der Funktion
USART_SEND(char) durchgeführt, woraufhin danach der ADC1 mit dem Befehl
ADC_Cmd(ADC1,ENABLE) wieder aktiviert wird. Zum Schluss wird die Variable readyToSend
zu Null gesetzt, sodass erst wieder wenn das Bit gesetzt wird diese Befehle erneut ausführt.
Die zweite if-Abfrage testet ob das ReceiveNotEmpty (RXNE) Bit gesetzt ist. Ist das der Fall,
wurde ein neuer Wert via USART empfangen und kann ausgelesen werden. Zu Beginn muss
das RXNE-Bit zurückgesetzt werden, sodass ein erneutes Setzen dieses Bits erneut einen Inter-
rupt auslöst. Das Zurücksetzten wird mit dem Befehl US-
ART_ClearITPendingBit(USART1,USART_IT_RXNE) durchgeführt. Schließlich wird der neu
empfangene Wert binär maskiert und in der zu Beginn deklarierte Variablen signalFromGUI
gespeichert.
5.3 Definition der Funktion USART_SEND()
Die Funktion USART_SEND() wurde mit folgendem Code implementiert:
void USART_SEND(char byte)
{
USART_SendData(USART1,byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
USART_SEND() sendet pro Aufruf jeweils eine 8-Bit Variable, die übergeben werden kann.
Das Senden an sich wird dabei von der Funktion USART_SendData(USART1,byte) durchge-
führt. Die darauffolgende while-Schleife fragt mit der Funktion US-
ART_GetFlagStatus(USART1, USART_FlAG_TC) kontinuierlich ab, welchen Wert das Trans-
mission Complete Flag (USART_FLAG_TC) hat. Dieses Flag kann die Werte SET und RESET
annehmen. Solange USART_FLAG_TC nicht gesetzt ist (RESET), verweilt das Programm in der
while-Schleife. Wird USART_FLAG_TC gesetzt (SET), springt das Programm aus der Schleife
und setzt die kommende Programmausführung fort.
5.4 Test der USART-Interrupt-Service-Routine
Analog zu der Implementierung der ADC-Interrupt-Service-Routine wird auch an dieser Stelle
getestet, ob das Programm wie gewünscht in die USART-Interrupt-Service-Routine springt.
Dazu wird ebenfalls der Debugger von Keil uVision verwendet. Führen Sie erneut die folgen-
den Schritte durch:
Klicken Sie auf ‘F7‘, um den geschriebenen Code in die Target-Files umzusetzen
Klicken Sie auf , um den Debugger zu starten
Setzten Sie einen Breakpoint in die DAC1-Interrupt-Service-Routine
Klicken Sie auf , um den Programmcode vom Debugger in den Kontroller zu laden
Klicken Sie auf , um das Programm zu starten
16 UART-Kommunikation zwischen Mikro-Controller-Plattform und PC
Das Programm sollte nun bei dem Breakpoint in der DAC1-Interrupt-Service-Routine stoppen.
Falls dem nicht so ist, muss die Konfiguration der DAC1-Schnittstelle sowie die Implementie-
rung der DAC1-Interrupt-Service-Routine auf Fehler untersucht werden.
5.5 Implementierung der Filtersteuerung
Nachdem getestet wurde, ob das Programm in die USART-Interrupt-Service-Routine springt,
kann mit der Implementierung der Filtersteuerung fortgefahren werden. Die Implementierung
der Filtersteuerung wurde in den Interrupt-Service-Routinen der USART1-Schnittstelle und des
ADC1 durchgeführt. In der USART-Interrupt-Service-Routine werden Steuersignale vom PC
empfangen und dazugehörige Steuervariablen gesetzt. Diese Variablen definieren, welcher Fil-
ter mit welchen Filtereigenschaften in der ADC-Interrupt-Service-Routine verwendet wird. Der
Test der Filter benötigt daher lediglich ein Terminalprogramm, mit welchem USART-Daten
vom PC an die Mikro-Controller-Plattform gesendet werden können und wird im Detail in Ka-
pitel 5.6 beschrieben.
An dieser Stelle wird auf Kapitel 6 verwiesen, wo der komplette Code aufgeführt ist. Der Ein-
fachheit halber wird in diesem Kapitel lediglich eine Kurzfassung der Filtersteuerung vorge-
stellt. Um die komplette Funktionsweise mit der MATLAB GUI zu testen, muss der Code, iden-
tisch wie er in Kapitel 7 aufgeführt ist, implementiert werden.
Zu Beginn des Programms wurden die folgenden Variablen definiert, die die Filtersteuerung
übernehmen:
volatile bool moving_avr = false; // 'i' (ASCII: 105)
volatile bool filter_rec = true; // 'j' (ASCII: 106)
volatile float GF = 0.5; // Gewichtungsfaktor GF
von 'filter_rec'
volatile int8_t M = 8; // Filterlänge M
von 'moving_avr'
Die Auswahl, welcher Filter aktuell verwendet wird, wurde über die bool-Variablen moving_avr
und filter_rec definiert. Bei dem Start des Programms ist das rekursive Tiefpass-Filter aktiv
(true) und der Moving-Average-Filter inaktiv (false). Die Variablen GF und M bestimmen das
jeweilige Übertragungsverhalten des aktuellen Filters.
Die Auswertung des empfangenen USART-Signals wurde, wie zuvor erwähnt, in der USART-
Interrupt-Service-Routine durchgeführt:
if(signalFromGUI==105){ // ASCII 'i'
filter.resetFilterCoefficients();
moving_avr=true;
filter_rec=false;
}
else if(signalFromGUI==106){ // ASCII 'j'
filter.resetFilterCoefficients();
filter_rec=true;
moving_avr=false;
}
else if((signalFromGUI>=0)&&(signalFromGUI<=100)){
if((filter_rec==true)&&(moving_avr==false))
GF=((float)signalFromGUI/100);
else if((filter_rec==false)&&(moving_avr==true))
M=(int8_t)((uint8_t)signalFromGUI-65);
}
UART-Kommunikation zwischen Mikro-Controller-Plattform und PC 17
Entspricht das via USART empfangene Signal dem ASCII-Wert ‘i‘ wird damit der Moving-
Average-Filter mit ‘true‘ ausgewählt , mit ‘j‘ dementsprechend das Tiefpass-Filter. Der jeweils
andere Filter wird mit ‘false‘ als inaktiv definiert. Zu Beginn wird in beiden Fällen jedoch erst-
mal die Funktion resetFilterCoefficients() aufgerufen, mit der die Speicherzellen der Filter in
den Ausgangszustand gesetzt werden. Ist das empfangene Signal ungleich dem ASCII-Wert für
‘i‘ oder ‘j‘ und in dem Wertebereich [0, 100], wird das empfangene Signal als eine Veränderung
der Filterkoeffizienten ausgewertet. Je nachdem, welcher Filter aktuell aktiv ist, wird das Signal
entsprechend ausgewertet. Ist das rekursive Tiefpass-Filter aktiv, wird das empfangene Signal
als Gewichtungsfaktor GF interpretiert. Ist wiederum der Moving-Average-Filter aktiv, wird das
empfangene Signal als Filterlänge M interpretiert.
Das angepasste Filter wird dann, wie bereits erwähnt, in der ADC-Interrupt-Service-Routine
verwendet.
5.6 Test der UART-Kommunikation mit Hyperterminal
Mit der Erklärung in Kapitel 5.5 kann nun die UART-Kommunikation zwischen dem PC und
der Mikro-Controller-Plattform getestet werden. Für diesen Test bietet sich ein Terminalpro-
gramm an. In diesem Projekt wurde dafür Hyperterminal verwendet. Die verwendete Version
von Hyperterminal ist unter folgendem Link zu finden:
http://www.eit.hs-karlsruhe.de/mesysto/teamorientierte-lehrmethoden/teamorientierte-
lehrmethoden/student-project-teams/zeitdiskrete-signale-und-systeme.html
Führen Sie folgende Schritte durch:
Verbinden Sie die USART-Schnittstelle der Mikro-Controller-Plattform mit dem PC
Überprüfen Sie welcher COM-Port reserviert wurde
o Start -> Systemsteuerung -> Geräten Manager -> Anschlüsse (COM &
LPT) -> Silicon Labs CP210x USB to UART Bridge (COMxx)
Öffnen Sie Hyperterminal und erstellen Sie eine Verbindung mit den folgenden Para-
metern:
o Connect using: COMxx
o Bits pro Sekunde: 57600
o Datenbits 8
o Parität keine
o Stoppbits 2
o Flusssteuerung keine
18 UART-Kommunikation zwischen Mikro-Controller-Plattform und PC
Abbildung 8: UART-Kommunikation via Hyperterminal
Die Verbindung sollte nun erstellt und aktiv sein. Laden Sie das Programm auf den Mikro-
Controller. Bei dem Start des Programms ist das rekursive Tiefpass-Filter aktiv. Sie können nun
mit ‘i‘ das Moving-Average-Filter aktivieren und mit ‘j‘ dementsprechend wieder das rekursi-
ven-Tiefpass-Filter. Die Filterkoeffizienten der Filter sind in diesem Moment noch auf dem
Standartwerten GF = 0,5 und M = 8. Funktioniert des Wechseln zwischen den Filtern wie er-
wartet, kann das Verändern der Koeffizienten getestet werden.
Ist das rekursive Tiefpass Filter aktiv wird das empfangene Signal als Gewichtungsfaktor GF
interpretiert. Dieser Gewichtungsfaktor kann Werte zwischen 0 und 1 in 0.01-Schritten anneh-
men. Das empfangene Signal für GF kann ASCII-Werte von 0 bis 100 annehmen die dement-
sprechend durch 100 geteilt werden. Aktivieren Sie das rekursive Tiefpass-Filter mit ‘j‘ und
senden Sie ein ‘Z‘ (ASCII: 90); GF wird daraufhin auf 0,9 gesetzt. Damit steigt die Zeitkonstan-
te des Filters an. Mit ‘Space‘ wird GF dementsprechend auf 0,32 gesetzt, wodurch die Zeitkon-
stante gesenkt wird. Die ersten 32 ASCII-Zeichen sind Steuersignale, können aber trotzdem für
den Test verwendet werden.
Ist das Moving-Average-Filter aktiv, wird das empfangene Signals als Filterlänge M interpre-
tiert. Die Filterlänge kann Werte zwischen 1 und 16 annehmen. Das empfangene Signal darf
ASCII-Werte von ‘B‘ (dez. 66) bis ‘Q‘ (dez. 81) annehmen, da von dem empfangenen Signal
der dezimal Wert 65 abgezogen wird. Stellen Sie sicher, dass Sie diesen Wertebereich nicht
überschreiten, da in dieser Implementierung keine Abfrage existiert, ob der empfangene Wert
zwischen 66 und 81 liegt.
5.7 Test der Filter mit Hilfe der MATLAB GUI
In Kapitel 4.3 und 5.6 wird der Funktionstest der Filter an sich, sowie der Test der Filtersteue-
rung beschrieben. Der in Kapitel 6 angehängte Code enthält jedoch noch weitere Funktionalitä-
ten, die an dieser Stelle kurz beschrieben werden. Zum Schluss wird dann die Steuerung dieser
Implementierung mittels MATLAB GUI diskutiert. Wie bereits in Kapitel 1 beschrieben wurde
UART-Kommunikation zwischen Mikro-Controller-Plattform und PC 19
ist die Verwendung der MATLAB GUI nur in Kombination mit der Messkarte BNC-2110 von
National Instruments möglich.
Der Hauptunterschied zu der bisher beschriebenen Funktionalität ist, dass die vollständige Im-
plementierung eine Messung des Filterverhaltens im Zeitbereich sowie im Frequenzbereich
erlaubt. Das Aktivieren der jeweiligen Messung erfolgt via UART-Schnittstelle. Eine Messung
im Zeitbereich wird gestartet wenn ein ‘g‘ empfangen wird. In diesem Fall wird die bool-
Variable start_zeitbereich auf true gesetzt. Eine Messung im Frequenzbereich wird dementspre-
chend mit dem Empfang eines ‘h‘ gestartet, woraufhin die bool-Variable start_frequenzbereich
auf true gesetzt wird. Bei Start des Programms sind beide Variablen auf false.
Bei einer Messung im Zeitbereich wird das aktuell empfangene Eingangssignal der Mikro-
Controller-Plattform außer Betracht gelassen. Anstelle dessen wird für eine Dauer von 60 bzw.
300 Abtastwerten ein konstanter Wert von 1375 an den Filter übergeben. Dies entspricht einem
Sprung von 0V auf 1V. Während der Dauer von 60 bzw. 300 Abtastwerten, wird der Aus-
gangswert des Filters in einem Array gespeichert. Dieses Array wird daraufhin Byte-weise via
USART1 an die MATLAB GUI gesendet, welche die Sprungantwort dann darstellt.
Abbildung 9: MATLAB GUI; rekursives Tiefpass-Filter, Messung Zeitbereich
Eine Messung im Frequenzbereich wird mit Hilfe der Messkarte BNV-2110 von National In-
struments durchgeführt. Dabei wird der Ausgang AO 0 der Messkarte mit ADC1 der Mikro-
Controller-Plattform verbunden und der DAC1 der Plattform mit dem Eingang AI 0 der Mess-
karte. Mit Hilfe der MATLAB GUI wird daraufhin eine Frequenz Sweep erzeugt, der über die
Messkarte an die Mirko-Controller-Plattform übergeben wird. Die Ausgangswerte des aktuell
verwendeten Filters werden mit Hilfe der Messkarte von MATLAB aufgezeichnet und in der
GUI als Amplituden- und Phasengang darstellt.
20 UART-Kommunikation zwischen Mikro-Controller-Plattform und PC
Abbildung 10: MATLAB GUI; rekursives Tiefpass-Filter, Messung Frequenzbereich
Abbildungsverzeichnis 21
6 Abbildungsverzeichnis
Abbildung 1: Reihenfolge der Implementierung 4
Abbildung 2:Konfiguration von Timer2, ADC1, DAC1 und der Zusatzbeschaltung 5
Abbildung 3: Implementierung der digitalen Tiefpass-Filter 9
Abbildung 4: Flussidagramm einer Filterimplementierung 9
Abbildung 5: Darstellung des Frequenzverhalten im Zeitbereich 12
Abbildung 6: Darstellung des Frequenzverhaltens mittels FFT 12
Abbildung 7: Konfiguration und Test der USART1-Schnittstelle 13
Abbildung 8: UART-Kommunikation via Hyperterminal 18
Abbildung 9: MATLAB GUI; rekursives Tiefpass-Filter, Messung Zeitbereich 19
Abbildung 10: MATLAB GUI; rekursives Tiefpass-Filter, Messung Frequenzbereich 20
22 Anhang 1: Quellcode
7 Anhang 1: Quellcode
#include "stm32f4xx.h"
#include <stdio.h>
#include "stm32f4xx_dac.h"
#include "stm32f4xx_adc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_dma.h"
#include "initDevice.h"
#include "Filter.h"
#include "stm32f4xx_usart.h"
#include "initTypes.h"
using namespace initTypes;
// Wert der Sprungantwort der via USART an die GUI gesendet wird
// 'Zeitbereich'
char Ua_Out[5];
// Zähler für die Zeitspanne in der 'Sprungantwort'/'Frequenzgang' aufgezeichnet wird
volatile uint16_t i_glob1 = 0;
// Zähler um 60/300 Werte (NbrOfSamples) im 'Zeitbereich' via USART an die GUI zu senden
volatile uint16_t i_glob2 = 0;
// Variablen um die Art der Messung zu definieren
volatile bool start_zeitbereich = false; // 'g' (ASCII: 103)
volatile bool start_frequenzbereich = false; // 'h' (ASCII: 104)
volatile bool zeitbereich_fertig = false;
// wird auf 'true' gesetzt, wenn 50/300 Werte (NbrOfSamples) konvertiert wurden
// wird auf 'false' gesetzt, wenn 50/300 Werte (NbrOfSamples) via USART versendet wurden
// Variablen um 'filter_rec' oder 'moving_avr' zu aktivieren
volatile bool moving_avr = false; // 'i' (ASCII: 105)
volatile bool filter_rec = true; // 'j' (ASCII: 106)
// Variablen, die das Verhalten von 'filter_rec' und moving_avr' definieren
// Diese Variablen können via USART von der GUI verändert werden
volatile float GF = 0.5; // Gewichtungsfaktor GF von 'filter_rec'
volatile int8_t M = 8; // Filterlänge M von 'moving_avr'
uint16_t NbrOfSamples = 60; // Anzahl der Abtastwerte der Sprungantwort
// Wird je nach Wert von GF zwischen 60 und 300 variiert
// Variablen für den ADC-Input und DAC-Output
static int16_t in=0;
uint16_t out;
// Puffer in dem 60/300 Werte (NbrOfSamples) der abgetasteten Sprungantwort gespeichert sind
// Werte sind als char[5] gespeichert -> 0.000
char Ua_Out_buffer[300][5];
// Variable um den Ausgang auf 1,5V anzuheben
int16_t offset = 0;
// DAC Flag
bool dacFlag;
// Testobjekt Filter
Filter filter;
//////////////////////////////////////////////////
// Funktionen für das Versendet von USART-Daten //
//////////////////////////////////////////////////
// 1 Byte versenden
void USART_SEND(char byte)
{
USART_SendData(USART1,byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET){
// Loop until the end of transmission
}
// Wait until Transmission Complete (TC)
}
// Versenden eines Puffers von Bytes
void USART_SEND_TEXT(char *buffer)
{
while (*buffer)
{
USART_SEND(*buffer);
buffer++;
}
}
Anhang 1: Quellcode 23
int main()
{
SystemInit();
initDevice device1;
///// Timer 2 /////
device1.setTimer(2);
device1.setfTimer(22050); //44kHz
device1.setTimerOutput(Trigger);
device1.timerInit();
///// ADC /////
device1.setAdc(1); // Wenn ADC1 ausgewählt wird, wird automatisch Kanal 13 festgelegt
device1.setADCTrigger(timer2ADC_TRGO);
device1.adcInit();
device1.setIRQChannel(adcInterrupt);
device1.nvicInit();
///// DAC /////
device1.setDac(1);
device1.setDacTrigger(None);
device1.dacInit();
dacFlag=false;
///// USART /////
device1.setUsartChannel(1);
device1.setUsartDmaConnection(usartDmaOff);
device1.usartInit();
device1.setIRQChannel(usartInterrupt);
device1.nvicInit();
///// Parametrisierung der Zusatzbeschaltung /////
device1.setADCInputPath(1,Bypass);
device1.setDACOutputPath(1,Bypass);
while(true)
{
}
return 0;
}
/*-------ADC Interrupt---------*/
extern "C" void ADC_IRQHandler(void)
{
static uint16_t count_uart = 0;
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
////////////////////////////////////
// Messung: Zeitbereich, Tiefpass //
////////////////////////////////////
if( start_zeitbereich == true && start_frequenzbereich == false &&
filter_rec == true && moving_avr == false)
{
in=1375;
out=filter.lowPassOrder1(in, GF);
snprintf(Ua_Out,6,"%f",(float)out); // Wandelt '(float)out' in einen String um "0.000"
for (int i=0; i<5;i++){
Ua_Out_buffer[i_glob1][i] = Ua_Out[i];
}
i_glob1++;
if(i_glob1==NbrOfSamples){
start_zeitbereich=false;
zeitbereich_fertig=true;
i_glob1=0;
}
}
else{
//////////////////////////////////////////
// Messung: Zeitbereich, Moving Average //
//////////////////////////////////////////
if( start_zeitbereich == true && start_frequenzbereich == false &&
filter_rec == false && moving_avr == true)
{
in=1375;
out=filter.movingAverage(in, M);
snprintf(Ua_Out,6,"%f",(float)out); // Wandelt '(float)out' in einen String um "0.000"
for (int i=0; i<5;i++){
Ua_Out_buffer[i_glob1][i] = Ua_Out[i];
}
24 Anhang 1: Quellcode
i_glob1++;
if(i_glob1==NbrOfSamples){
start_zeitbereich=false;
zeitbereich_fertig=true;
i_glob1=0;
}
}
else{
////////////////////////////////////////
// Messung: Frequenzbereich, Tiefpass //
////////////////////////////////////////
if( start_zeitbereich == false && start_frequenzbereich == true &&
filter_rec == true && moving_avr == false)
{
in=ADC1->DR;
out=filter.lowPassOrder1(in, GF);
offset=0;
DAC_SetChannel1Data(DAC_Align_12b_R, out+offset);
}
else{
//////////////////////////////////////////////
// Messung: Frequenzbereich, Moving Average //
//////////////////////////////////////////////
if( start_zeitbereich == false && start_frequenzbereich == true &&
filter_rec == false && moving_avr == true)
{
in=ADC1->DR;
out=filter.movingAverage(in, M);
offset=0;
DAC_SetChannel1Data(DAC_Align_12b_R, out+offset);
}
else{
///////////////////////////////////////////////
// Keine Messung: Nur Filterung mit Tiefpass //
///////////////////////////////////////////////
if( start_zeitbereich == false && start_frequenzbereich == false &&
filter_rec == true && moving_avr == false)
{
in=ADC1->DR;
out=filter.lowPassOrder1(in, GF);
offset=0;
DAC_SetChannel1Data(DAC_Align_12b_R, out+offset);
}
else{
/////////////////////////////////////////////////////
// Keine Messung: Nur Filterung mit Moving Average //
/////////////////////////////////////////////////////
if( start_zeitbereich == false && start_frequenzbereich == false &&
filter_rec == false && moving_avr == true)
{
in=ADC1->DR;
out=filter.movingAverage(in, M);
offset=0;
DAC_SetChannel1Data(DAC_Align_12b_R, out+offset);
}
}
}
}
}
}
}
/*-------USART Interrupt---------*/
extern "C" void USART1_IRQHandler(void)
{
uint8_t signalFromGUI;
// 'zeitbereich_fertig' -> Es wurden 50/300 Werte (NbrOfSamples) aufgezeichnet
// Diese Werte werden via USART an die GUI gesendet
// In diesem Zeitraum wird der ADC1 inaktiv geschaltet (DISABLE)
if (zeitbereich_fertig){
ADC_Cmd(ADC1,DISABLE);
if (i_glob2<NbrOfSamples){
USART_SEND(Ua_Out_buffer[i_glob2][0]);
USART_SEND(Ua_Out_buffer[i_glob2][1]);
USART_SEND(Ua_Out_buffer[i_glob2][2]);
USART_SEND(Ua_Out_buffer[i_glob2][3]);
USART_SEND(Ua_Out_buffer[i_glob2][4]);
i_glob2++;
}
Anhang 1: Quellcode 25
}
// Wenn 50/300 Werte (NbrOfSamples) gesendet wurden wird als Schlusszeichen ':' gesendet
// ADC1 wird daraufhin wieder aktiv geschalten (ENBALE)
if(i_glob2==NbrOfSamples){
USART_SEND(':');
ADC_Cmd(ADC1,ENABLE);
i_glob2=0;
zeitbereich_fertig=false;
start_zeitbereich=false;
}
// RXNE == 0 -> RESET -> Es wurden keine Daten empfangen
// RXNE != 0 -> Es wurden Daten empfangen und diese können gelesen werden
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
// Pendig Bit 'USART_IT_RXNE' löschen
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
signalFromGUI = (USART_ReceiveData(USART1) & 0x7F);
// In der GUI wurde Sprungantwort ausgewählt 'g'
if(signalFromGUI==103){
// 50/300 Werte (NbrOfSamples) aufzeichnen und via USART and die GUI senden
filter.resetFilterCoefficients();
start_zeitbereich=true;
start_frequenzbereich=false;
}
// In der GUI wurde Frequenzgang ausgewählt 'h'
else if(signalFromGUI==104){
if(start_frequenzbereich==false){
start_frequenzbereich=true;
}
else{
start_frequenzbereich=false;
}
start_zeitbereich=false;
filter.resetFilterCoefficients();
}
// In der GUI wurde im Dropdownmenü 'Moving Average' ausgewählt 'i'
else if(signalFromGUI==105){
filter.resetFilterCoefficients();
moving_avr=true;
filter_rec=false;
}
// In der GUI wurde im Dropdownmenü 'Tiefpassfilter' ausgewählt 'j'
else if(signalFromGUI==106){
filter.resetFilterCoefficients();
filter_rec=true;
moving_avr=false;
}
// Der Wert für GF oder M wurde in der GUI verändert
// Der Datentyp des empfangenen Wertes wird angepasst
// Die Anzahl der Abtastwerte der Sprungantwort wird definiert (50/300)
else if((signalFromGUI>=0)&&(signalFromGUI<=100)){
if((filter_rec==true)&&(moving_avr==false)){
GF=((float)signalFromGUI/100); // 0/100 = 0
// 50/100 = 0.5
// 100/100 = 1
if(GF<=0.9)
NbrOfSamples=60;
else{
if((GF>0.9)&&(GF<=1))
NbrOfSamples=300;
}
}
else if((filter_rec==false)&&(moving_avr==true)){
M=(int8_t)((uint8_t)signalFromGUI-65); //(66-65) = 1
//(75-65) = 10
//(81-65) = 16
NbrOfSamples=60;
}
}
}
}