Forum: Digitale Signalverarbeitung / DSP / Machine Learning FIR-Filter richtig auf STM32 implementieren


von Mario (Gast)


Lesenswert?

Hallo,

Ich möchte auf meinem STM32F429-Discovery Board eine Filterung 
durchführen.

Das Signal wird mit einer konstanten Sampling Rate (realisiert durch 
einen Timer)folgendermaßen eingelesen:

// ---------------------------------------------------------------------

#include "system.h"
#include <stdlib.h>
#include "stm32_ub_usb_cdc.h"
#include "stm32_ub_led.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "misc.h"
#include "stm32_ub_adc1_single.h"
#include "stm32_ub_lcd_ili9341.h"
#include "stm32_ub_graphic.h"


int main(void)
{

    UB_ADC1_SINGLE_Init();
    UB_USB_CDC_Init();
    SystemInit();
    delay_init();
    sdram_init();
    ltdc_init();
    ili9341_init();
    UB_Led_Init();
    UB_USB_CDC_Init();


   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);

   // Timer7 konfigurieren
   TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
   TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
   TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
   TIM_TimeBase_InitStructure.TIM_Period = 28;
   TIM_TimeBase_InitStructure.TIM_Prescaler = 1000;
   TIM_TimeBaseInit(TIM7, &TIM_TimeBase_InitStructure);
   TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);

   // Timer7 einschalten
   TIM_Cmd(TIM7, ENABLE);

   // Interruptcontroller konfigurieren
   NVIC_InitTypeDef NVIC_InitStructure;
   NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
   NVIC_Init(&NVIC_InitStructure);

while(1)
{}

}

void TIM7_IRQHandler()
{
int x =  0;
char str[4];
x = UB_ADC1_SINGLE_Read_MW(ADC_PC3);
sprintf(str, "%d", x);
UB_USB_CDC_SendString(str,LFCR);
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
}

//----------------------------------------------------------------------

Der Interrupt wird mit einer Frequenz von 3kHz aufgerufen und sendet den 
ADC-Value über USB an die serielle Schnittstelle. Dies klappt 
einwandfrei.


Jetzt möchte ich die eingelesenen Werte in "Echtzeit" filtern.


Ich habe mir mit MatLab und dem FDATOOL ein Lowpass FIR Filter designt 
um die Koeffizienten zu bekommen.

Ordnung: 50
Fs: 3000
Fpass: 80
Fstop: 120


Mein Ansatz um das Filter zu implementieren:

//---------------------------------------------------------------------
int i  = 0;
int ntaps = 51;
float buffer[51];
float h[] =


float h[] =
{
-0.00091909820848026845,
-0.0027176960266135364 ,
-0.0024869527598547769 ,
 0.0036614383834879848 ,
 0.013650925230654689  ,
 0.017351165901097854  ,
 0.0076653061904350594 ,
-0.0065547188696257145 ,
-0.0076967840370497573 ,
 0.0061054594214059454 ,
 0.013873915748642305  ,
 0.00035086172829532027,
-0.01690892543668587   ,
-0.0089056427491534841 ,
 0.017441129500858993  ,
 0.02074504452761099   ,
-0.012296494251941141  ,
-0.034240865909579518  ,
-0.00103452960557321   ,
 0.047790305520800068  ,
 0.027363037914845938  ,
-0.059379518831047785  ,
-0.082307025929228672  ,
 0.067186909432870937  ,
 0.31001517709024967   ,
 0.43004788034351471   ,
 0.31001517709024967   ,
 0.067186909432870937  ,
-0.082307025929228672  ,
-0.059379518831047785  ,
 0.027363037914845938  ,
 0.047790305520800068  ,
-0.00103452960557321   ,
-0.034240865909579518  ,
-0.012296494251941141  ,
 0.02074504452761099   ,
 0.017441129500858993  ,
-0.0089056427491534841 ,
-0.01690892543668587   ,
 0.00035086172829532027,
 0.013873915748642305  ,
 0.0061054594214059454 ,
-0.0076967840370497573 ,
-0.0065547188696257145 ,
 0.0076653061904350594 ,
 0.017351165901097854  ,
 0.013650925230654689  ,
 0.0036614383834879848 ,
-0.0024869527598547769 ,
-0.0027176960266135364 ,
-0.00091909820848026845
}


for(i=ntaps-1;i>0;i--)
  {
    buffer[i]=buffer[i-1];   //macht Platz für neuen Wert
  }

    buffer[0] = UB_ADC1_SINGLE_Read_MW(ADC_PC3);

    for(i = 0;i<ntaps;i++)
    {
    x += buffer[i]*h[i];                //berechnet Ausgangswert
    }



Leider weiß ich nicht wie ich das in das obere Programm richtig einfüge, 
da ich ja eine konstante Sampling Rate brauche?
Mach ich die Filterung in der while? oder alles im Interrupt?

Vielen Dank für eure Hilfe!
Ist leider mein erstes digitales Filter :/

: Verschoben durch Admin
von Jan K. (jan_k)


Lesenswert?

In der ISR sollte auf rechenintensive Operationen verzichtet werden, 
z.B. auch auf das sprintf.
Normalerweise wird eine Flag (die volatile sein muss) in der ISR gesetzt 
und in der main loop überprüft. Sobald ein neues sample da ist, rechnest 
du den nächsten Schritt deines Filters in der main und setzt dann 
entweder eine neue flag oder benutzt eine state machine, um anzuzeigen, 
dass du deinen gefilterten Wert über USB verschicken kannst.

von Stefan (Gast)


Lesenswert?

Im Cube von ST in der DSP-Library gibt es FIR-Filter-Implementierungen. 
Die sind ganz gut optimiert.

Hast Du schon einmal ein Multitasking-System ausprobiert? FreeRTOS gibt 
es angepasst für die STM32F4-Familie. Dann kannst Du Dein Sampling, 
FIR-Filter und Daten weiterleiten in eine Task packen, die nichts 
anderes zu tun hat. Alle anderen Aufgaben arbeitest Du in anderen Tasks 
ab.

Gruß, Stefan

von Jan K. (jan_k)


Lesenswert?

Da der OP offensichtlich kein Profi ist und eine relativ simple Aufgabe 
erledigen möchte (und vor allem nicht zig Sachen gleichzeitig, sonst 
hätte er uns das möglicherweise verraten), ist ein realtime OS sowas von 
überflüssig und erschlägt jemanden, der obiges nicht per Hand 
programmieren kann. Ebenso braucht man für ein einziges FIR mit 
Sicherheit nicht die DSP Lib, ein Cortex M4 lacht sich schlapp bei 3 
kHz, sodass keine hochoptimierten Algos nötig sein sollten.
Sollen mehrere Filter implementiert werden stimme ich zu, dass man ein 
Modul für das Filter verwenden, das man instanziieren kann. Das kann aus 
der Lib kommen, ist aber auch sehr fix selbst geschrieben.
Ich glaube aber soweit sind wir hier noch gar nicht.

Schöne Grüße,
Jan

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Vor allem möchte ich mal ernsthaft anzweifeln, dass wirklich ein FIR 
50ster (sic!) Ordnung benötigt wird... aber ich mag mich ja täuschen.

von Nils P. (ert)


Lesenswert?

Michael Reinelt schrieb:
> Vor allem möchte ich mal ernsthaft anzweifeln, dass wirklich ein FIR
> 50ster (sic!) Ordnung benötigt wird... aber ich mag mich ja täuschen.

Kann ich mir auch kaum vorstellen. Was willste den der TO damit 
erreichen?

Und der STM den junkt ein Filter in der ISR net. Sollte man aber 
trotzdem mit einem Oszi überprüfen.

Schau dir auch mal den Code an, was für Assembler-Befehle der Compiler 
raushaut. Ist die Floating-Point-unit an? Kannst ja auch mal im Debugger 
dir die Werte anschauen.

Wenn du schon Matlab nutzt, hau das Filter in eine Embedded-Matlab-Fkt 
in Simulink und teste am PC, ob das Filter mit double float richtig 
arbeitet.

Gruß
Ert

von Mario (Gast)


Lesenswert?

Vielen Dank für die Antworten!

Das TP-Filter 50 Ordnung ist nur mal testweise.

Spaeter will ich ein Notchfilter für 50Hz realisieren, wo ich laut dem 
generierten Bodediagramm eine hohe Ordnung brauche für ein gutes 
Ergebnis.


Ich würde auch gerne mal das Filter einfach realisieren um es zu 
verstehen. Die Vorschläge schau ich mir natürlich an wenn ich das mal am 
laufen habe.

Der Test mit Simulink ist eine gute Idee und werd ich mir anschaun.

von Mario (Gast)


Lesenswert?

Ich hab jz mal das Tiefpass - Filter am Laufen =)

Jedoch möchte ich jetzt noch ein 50Hz Notch Filter zusätzlich einbauen.

Wenn ich aber ein FIR-Notch-Filter in Matlab berechnen lasse, komme ich 
auf Ordnungen >500 für ein akzeptables Filter (Stopband -40dB, 
Welligkeit <1dB,..)

Ist das normal oder gibts dafür bessere Lösungne? (IIR-Filter,..)

Vielen Dank!!

von Tobias P. (hubertus)


Lesenswert?

Moin moin,

anstatt 50. Ordnung FIR zu verwenden, solltest du in der Tat in Erwägung 
ziehen, ein IIR Filter zu bauen. Die Ordnung wird sofort signifikant 
sinken. Wichtig ist, dass du das IIR nicht einfach 'straight forward' 
implementierst, sondern in eine SOS-Form überführst, denn aufgrund von 
Rundungsfehlern der Koeffizienten kann IIR Instabil werden. Mit SOS 
kannst du das einigermassen einfach noch überprüfen.

Zudem würde ich anstatt double nur einfache Genauigkeit benutzen, denn 
die M4 FPU kann m.E. kein double.

Guck mal hier, ichhab hier was für matlab:

http://www.kooltek.net/mikrocontroller/realisierung-digitaler-filter

von Hari12 (Gast)


Angehängte Dateien:

Lesenswert?

Ich hab mich jz entschieden die 50Hz-Filterung direkt in MatLab 
durchzuführen.

Ich fülle immer ein Array mit variabler Länge mit den Samples von der 
seriellen Schnittstelle und plotte diese dann.

Vor dem Plotten filtere ich dieses Array noch mit einem Matlab-IIR 
Filter.

Habe 3 Bilder angehängt, welche meines "Problem" schön zeigen:
Ein 50Hz Sinussignal wird sehr gut gefiltert, aber hat leichte 
Überschwinger die je nach Güte des Filters zunehmen.

Natürlich sieht man diese Überschwinger dann auch bei z.B 90Hz (siehe 
Bild).


Mir ist klar dass sich das Filter erst einschwingen muss, was man hier 
halt sehr schön sieht.
Da ich die Filterfunktion aber sehr oft Aufrufe (immer nach einer 
einstellbaren Anzahl von Samples) sieht man dieses Einschwingen leider 
bei je nachdem wie viele Frames ich darstelle immer sehr deutlich.


Habt ihr irgent einen Idee dieses Schwingen zu vermeiden bzw. nicht 
darzustellen?

Würde es funktionieren wenn ich den Anfang meines Arrays mit 
Dummy-Werten fülle. Bei welchen dann der Einschwingvorgang stattfindet 
und ich aber nur die dahinterligenden "richtigen" Werte plotte??

Vielen Dank!!

von Nils P. (ert)


Lesenswert?

Wie sollen wir die Qualität 'beurteilen', wenn du das Eingangssignal 
nicht auch darstellst?

Wenn da jetzt +- 1000000000 reingeht sind alle Filter unbrauchbar -.-

Lade deine .mat Daten hoch, evtl kann hier ja einer helfen...

von Mario (Gast)


Lesenswert?

Das Eingangssignal ist das Bild "50Hz_ohneNotch".

Das Bild "50Hz_mitNotch" zeigt das selbe Eingangssignal nur mit aktivem 
Filter.

Das Bild "90Hz_mitNotch" soll zeigen, dass auch bei Signalen außerhalb 
der Notchfrequenz diese Welligkeit am Anfang auftritt,wenn das Filter 
eingeschaltet ist =)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.