mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik IIR Besselfilter in Attiny85 - 32 Bit Berechnung möglich?


Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
ich habe einen IIR-Besselfilter gebaut und mit den Messwerten aus der 
Schaltung in Excel ausprobiert. Dort funktioniert der wunderbar.

Nun hat der ATtiny85 meines Wissens keine Gleitkommaberechnung und nicht 
genug Platz für die double-Bibliothek, also habe ich noch ein Excelsheet 
und einen Tag drangehängt und die Berechnung komplett ohne negative 
Zahlen und ohne Gleitkomma möglich gemacht. Die habe ich dann 
ausführlich mit unechten und mit realen Messergebnissen getestet. Siehe 
da, rein theoretisch funktioniert es schon mal.

Leider sehe ich keine Möglichkeit, das auch noch in 16 Bit 
reinzuquetschen. Und das ist der Haken.

Die Filterkoeffizienten haben teilweise ein 10^-5 dranhängen. Sprich, 
ohne Kommazahlen merkt man erst dann was von dem Koeffizienten, wenn man 
die Berechnung mit ungefähr 10^5 multipliziert oder um 15 bis 16 Bit 
nach links schiebt. Wenn ich das mit den Messergebnissen mache, habe ich 
leider umgehend die 16 Bit gesprengt.

Der Attiny85 ADC liefert mir 10 Bit Messergebnisse. Ich habe uint32_t 
verwendet. Meine Berechnung funktioniert scheinbar im Attiny nicht.

Hier die Frage:
Kann man mit dem Datentyp uint32_t auch rechnen? Wenn nicht, gibt es 
eine Bibliothek oder irgendwas zum runterladen, so dass man dem Attiny 
auch 32 Bit beibringen kann?

Ich habe den ganzen Tag gegoogelt aber nichts gefunden. Vielleicht bin 
ich auch nur zu blind um den Baum im Wald zu finden. Kann mir einer von 
euch helfen?

Autor: Oliver Döring (odbs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Poste mal deinen Quelltest. Mit großer Wahrscheinlichkeit hast du bei 
der Umsetzung in C die klassischen Fehler gemacht.

Der avr-gcc kann jedenfalls Maschinencode für Berechnungen mit uint32_t 
erzeugen!

Autor: Zwölf Mal Acht (hacky)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es sollte nicht allzu schwierig sein mit 32 bit auf einem Tiny 85 
rechnen zu wollen. Solange es nicht allzu viele Koeffizienten sind...

Autor: Andreas Wagener (calidus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke
Hier ist der Code.
Er ist ein bisschen lang mit seinen Kommentaren, darum habe ich ihn 
erstmal nicht gepostet gehabt.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h> 

//PB2: ADC1 ist der Eingang für die Fotodiode.
//PB0: ist der Ausgang für den Motor.
//PB4: PCINT4 ist der Steuerausgang für die Fotodioden-Beleuchtung

// Wenn der Motor dreht, ziehen abwechselnd schwarze und weiße Streifen an
// der Fotodiode vorbei. Bei sieben gezählten Streifen soll er wieder anhalten.
#define ANZAHL_STREIFEN 7
char schwarze_streifen; //Zähler für die schwarzen Streifen
char wirdsHeller; //wird es heller oder dunkler?


// Wenn der Motor steht, soll das Teil 5 Minuten Pause machen bis er wieder anfängt
// zu drehen.
#define PAUSENLAENGE 600 // halbe Sekunden
unsigned int pausenzeit; //zähler für die halben Sekunden


// generelle Statemachine im Hauptprogramm
#define WARTEN_AUF_MESSUNG  0
#define MESSEN_STARTEN  2
#define MESSEN_FERTIG  3

#define PAUSE_EINLAEUTEN 6
#define PAUSE 4
#define WEITERDREHEN 5

char cSREG;


//Filtervariablen
uint32_t mess_hilf, mess_0, mess_1, mess_2, mess_3, mess_4;
uint32_t ergebnis_4, ergebnis_3, ergebnis_2, ergebnis_1, ergebnis_0;
uint32_t A, B; // Filter zwischenergebnisse

char communication; //Statemachine Steuervariable


ISR(TIM1_COMPA_vect) // Timer 1 meldet einen OCR1A Vergleich
{
    cSREG = SREG;
    // Interrupt Code 
  if (communication == PAUSE)
  {
    pausenzeit = pausenzeit + 1;
    
    if (pausenzeit >= PAUSENLAENGE)
    {
      communication = WEITERDREHEN;
    }
  }
  else
  {
    communication = MESSEN_STARTEN; //Messintervall vorüber
  }

  SREG = cSREG;
  //TIFR = 0x7E;
  sei();
}

//Der ADC meldet sich hier, wenn die Messung fertig ist.
ISR (ADC_vect)
{
  communication = MESSEN_FERTIG;
  sei();
}


void adc_einstellen(void);
void messwert_filtern(void);

void timer1_einstellen(unsigned char); // umstellen von Pause zur Messen und andersherum

void motor_ein(void); // ein
void motor_aus(void); // aus

int main(void)
{
   // Initialisierungen: globale Variablen

  //Vor-Initialisierung, damit der Filter schneller auf Stand kommt
  ergebnis_4 = 32768;  
  ergebnis_3 = 32768;
  ergebnis_2 = 32768;
  ergebnis_1 = 32768;
  ergebnis_0 = 32768;

  //Statemachine initialisieren; mit "drehen" starten.
  communication = WEITERDREHEN;

  wirdsHeller = 2; //wird es heller oder dunkler?
  schwarze_streifen = 0; //wie viele Streifen sind am Sensor vorbeigezogen?
  pausenzeit = 0; //Aktuell verstrichene Pausenzeit

  // Initialisierungen: Clock, Ports, Timer, ADC, Interrupts
  // Clock
  // Der interne Takt ist eingestellt auf 8 MHz 


  // Watchdog deaktivieren
  //  WDTCR = 0x00;


  // Portpin
  DIDR0 = 0x00;
  MCUCR = 0x00; // Pull Up Widerstände aktivieren
  DDRB  = 0x3F; // Data Direction Register, vorerst komplett als Ausgang
  PORTB = 0x00; // Alle Pins auf Low
  //PINB      // Eingangsregister -> will ich nicht -> weggelassen

// nötige Peripherals einstellen
  timer1_einstellen(WEITERDREHEN); 
  adc_einstellen();
  pwm_einstellen();

// LED einstellen
  DDRB |= (1 << PB4); // Pin für LED auf "Ausgang" schalten



// Timer-Interrupts aktivieren
  TIMSK = 0x40; // Timer 1 OCR1A Compare Match Interrupt enabled
  SREG |= 0x80;

   
/*********************************************************************************/   
   
   
   while(1) { 
     // Statemachine              
    switch (communication)
    {
/**************************************************/
    case PAUSE: { 
    // Warte auf nächsten Zyklusstart; 
    // State wird im Interrupt verändert

      break;
    }
/**************************************************/
    case WEITERDREHEN: { // Start eines neuen Zyklus
      // Sensorbeleuchtung starten
      PORTB |= (1 << PB4); // LED für den Sensor einschalten
      // Timer 1 umstellen für Sensor Interrupts
      timer1_einstellen(WEITERDREHEN);
      // Motor starten
      motor_ein();
      // Messung starten
      communication = MESSEN_STARTEN;
      break;
    }
/**************************************************/
    case MESSEN_STARTEN: { //Messung starten

      ADCSRA |= (1<<ADSC); // ADSC auf 1 setzen startet den ADC
      communication = WARTEN_AUF_MESSUNG;
      break;

    }
/**************************************************/
    case WARTEN_AUF_MESSUNG: { 
    // Wartet auf nächstes Messwert-Timer-Intervall oder auf
    // die Fertigstellung der Messung. Beide States werden 
    // in den Interrupt Handlern gesetzt

      break;
    }
/**************************************************/
    case MESSEN_FERTIG: //Messwert fertig gemessen
    {

      mess_0 = ADCL; 
      mess_hilf = ADCH;
      mess_hilf = mess_hilf * 256;
      mess_0 = mess_0 + mess_hilf;
      messwert_filtern();
      if (ergebnis_0 > ergebnis_4)
      {
        wirdsHeller = wirdsHeller + 1;
        if (wirdsHeller == 4) //einige Male in Folge heller geworden?
        {  //-> Ein schwarzer Streifen ist vorbeigezogen. Zählen!
          schwarze_streifen = schwarze_streifen + 1;
        }
        if (wirdsHeller > 5)
        {
          wirdsHeller = 5;
        }

      }
      if (ergebnis_0 < ergebnis_4)
      {  // Es wird dunkler
        wirdsHeller = 0;
      }
      if (schwarze_streifen >= ANZAHL_STREIFEN)
      {
        communication = PAUSE_EINLAEUTEN;
      }
      else communication = WARTEN_AUF_MESSUNG;

      break;
    }
/**************************************************/
    case PAUSE_EINLAEUTEN:
    {
      // Motor stoppen
      motor_aus();
      // LED ausschalten
      PORTB &= ~(1 << PB4); // LED für den Sensor ausschalten
      // Pause machen bis zum nächsten Mal
      communication = PAUSE;
      // Timer 1 umstellen auf Pausenmodus
      timer1_einstellen(PAUSE);
      communication = PAUSE;
      break;
    }
/**************************************************/
    default : {
      communication = PAUSE_EINLAEUTEN;
      // Im Fehlerfall lieber den Motor stoppen und in sichere States gehen
      break;
      }
    }
  }                        
 /***********************************************************************/
   return 0;    
}

/*************************************************************
Ausgelagerte ADC-Einstellen-Funktion für die Übersichtlichkeit
*************************************************************/
void adc_einstellen(void)
{
  /*
      #define ADMUX   _SFR_IO8(0x07)
      #define REFS1   7
      #define REFS0   6
      #define ADLAR   5
      #define REFS2   4
      #define MUX3    3
      #define MUX2    2
      #define MUX1    1
      #define MUX0    0
      */  
  ADMUX = 0xE1; //Interne 1.1V Referenz, ADLAR = 1 für linksbündige Ergebnisse, Pin PB2 (ADC1D) als ADC-Eingang

  /*
    ADCSRB
    Bit 7: Bei 1 wird bipolar gemessen. 0.
    Bit 5: Bei 1 wird der Eingang negativ gemessen. 0.
    Bits 2:0 : Auto Trigger Source. Brauche ich nicht, weil ADATE = 0 ist.
  */
  ADCSRB = 0x00;
  // ADC1 Pin als Eingang schalten
  DIDR0 |= (1<<ADC1D);   // Digital Input Disable Register;  Bit 5 auf 1, damit der AD-Pin nicht digital verwendet wird.
  DDRB  &= ~(1<<ADC1D); //Pin ADC1D als Eingang
  /* 
    ADCSRA
    Bit 7, ADEN: ADC Enable. Auf 1.
    Bit 6, ADSC: ADC Start Converstion. Auf 1 zum Initialisieren vom AD
    Bit 5, ADATE: ADC Auto trigger enable: Bei bestimmten Ereignissen automatisch starten. 0 für Aus.
    Bit 4, ADIF: ADC Interrupt Flag, wird automatisch beschrieben wenn der ADC fertig ist
    Bit 3, ADIE: ADC Interrupt Enable, auf 1 wenn ein Interrupt nach der Wandlung ausgeführt werden soll. 1.
    Bit 2:0, ADPS2:0, ADC Prescaler Bits für System Clock. 110 für 8MHz geteilt durch 64 = 125kHz
  */
  ADCSRA = 0xCE; //führt auch gleich die erste Konversion für die Initialisierung mit durch
}

/*************************************************************
Ausgelagerte Motor-Steuern-Funktion für die Übersichtlichkeit
Benötigt OCR0A als PWM Ausgang vor-eingestellt
*************************************************************/

void motor_ein(void)
{
  PORTB |= (1 << PB0); //Eins auf den Motorpin
}
void motor_aus(void)
{
  PORTB &= ~(1 << PB0);//Null auf den Motorpin
}

/*************************************************************
Ausgelagerte Pausentimer-Einstellen-Funktion für die Übersichtlichkeit
Der Pausentimer soll messen, wann die Pause vorbei ist.
Er soll außerdem den ADC Takt vorgeben wenn keine Pause ist.
Die Funktion soll nur aufgerufen werden um den Timer-Modus umzustellen!
*************************************************************/
void timer1_einstellen(unsigned char state)
{
  if (state == PAUSE)
  {
    GTCCR = 0x80; // Stoppen vom Timer via setzen von 1 im TSM Bit
    TCCR1 = 0x8F; // Clear on compare match mit OCR1C; Sysclock/16384 ist die Timer Clock (488 Hz)
    OCR1A = 0x0A; // 0x0A = 10; an dieser Zählmarke wird der OCR1A-Interrupt aktiviert.
    OCR1C = 244;  // Nach 244 Zählern wird der Timer rückgesetzt -> 2 Interrupts / sec
    GTCCR = 0x00; // kein PWM B, kein Output force, kein löschen von TCCR1, 0 ins TSM Bit -> restart
  }
  else
  {  
    GTCCR = 0x80; // Stoppen vom Timer via setzen von 1 im TSM Bit
    TCCR1 = 0x8A; // Clear on compare match mit OCR1C; Sysclock/512 ist die Timer Clock (15625 Hz)
    OCR1A = 0x0A; // 0x0A = 10; an dieser Zählmarke wird der OCR1A-Interrupt aktiviert.
    OCR1C = 31;   // Nach 31 Zählern wird der Timer rückgesetzt -> 504 Interrupts / sec
    GTCCR = 0x00; // kein PWM B, kein Output force, kein löschen von TCCR1, 0 ins TSM Bit -> restart
  }    
}


/****************************************************************************
Messwerte filtern

Benötigt:
- 5 Ergebnisvariablen mit uint32_t
- 5 Messwertvariablen mit uint32_t
- Messwerte 16 Bit breit linksbündig (sorry)
- Abtastfrequenz 500Hz
- Filtervariablen 32 Bit breit

 Die Filterkoeffizienten wurden mit dem Excel aus dem Projektordner bestimmt.
 Es handelt sich um ein IIR Filter mit einer Grenzfrequenz von 12,5Hz
 und 4 Filterstufen nach Butterworth

*****************************************************************************/
void messwert_filtern(void)
{

  A =   12 * mess_0; //TAP 0
  A = A + 50 * mess_1; //TAP 1
  A = A + 75 * mess_2; //TAP 2
  A = A + 50 * mess_3; //TAP 3
  A = A + 12 * mess_4; //TAP 4
  
  A = A + 50; // Faktor entfernen I: die Hälfte vom Faktor drauflegen gegen Rundungsfehler
  A = (int)(A/100); //Faktor entfernen II: Teilen und abrunden
  
  B =   14359 * ergebnis_1; //TAP 1
  B = B - 19405 * ergebnis_2; //TAP 2
  B = B + 11696 * ergebnis_3; //TAP 3
  B = B - 2652  * ergebnis_4; //TAP 4

  ergebnis_0 = A + B + 2000;// Faktor entfernen I: die Hälfte vom Faktor drauflegen gegen Rundungsfehler
  ergebnis_0 = (int)(ergebnis_0 / 4000); //Faktor entfernen II: Teilen und abrunden

  //Messwerte durch die TAPs schieben
  mess_4 = mess_3;
  mess_3 = mess_2;
  mess_2 = mess_1;
  mess_1 = mess_0; //mess0 wird vom ADC gefüllt
  
  //Ergebnisse durch die TAPs schieben
  ergebnis_4 = ergebnis_3;
  ergebnis_3 = ergebnis_2;
  ergebnis_2 = ergebnis_1;
  ergebnis_1 = ergebnis_0; //ergebnis_0 wird weiter oben berechnet.
}


Autor: gasd12 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich würde dir zum einen das
http://www.mikrocontroller.net/articles/Festkommaarithmetik
empfehlen und dann mal die frage sind adcl und adch nicht uint16_t ???

Autor: Helmut Lenzen (helmi1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fuer sowas nimmt man die sogenannten Q-Formate.

http://en.wikipedia.org/wiki/Q_%28number_format%29

Auch darf die Samplefrequenz und deine hoechste Signalfrequenz nicht zu 
weit auseinander liegen. Je weiter die auseinander liegen um so kleiner 
werden deine Filterkonstanten.

Autor: eProfi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
0. Wichtige Regeln - erst lesen, dann posten!
> Groß- und Kleinschreibung verwenden
> Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang


1. Erzähl doch mal bitte in Prosa, was das Ding machen soll.

2. Es erscheint mir alles viel zu kompliziert hingeschrieben.
Meine Meinung: man kann das ganze Problem in 30 Zeilen lösen.

3. IIR zum "Entprellen" der Fotozelle?
Warum machst Du nicht gleich noch eine Fourie-Analyse?
In diesem Fall reicht doch ein Einfachst-Filter.
Oder übersehe ich etwas?

4.
> A = (int)(A/100); //Faktor entfernen II: Teilen und abrunden
> ergebnis_0 = (int)(ergebnis_0 / 4000); //Faktor entfernen II

Beitrag "Re: Möglichst Resourcenschonend programmieren"
Beitrag "Re: sinus LUT über Interrupt auslesen"
-->
> A = (int)(A/128); //Faktor entfernen II: Teilen und abrunden
> ergebnis_0 = (int)(ergebnis_0 / 4096); //Faktor entfernen II
Die vorherigen Faktoren müssen natürlich ebenfalls angepasst werden.

Autor: Andreas Wagener (calidus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das mit der Festkommaarythmetik/Q-Format finde ich gut. Danke für den 
Tipp, die waren mir nicht bekannt.

@Helmut: Leider muss ich den Filter so gestalten wie er ist, da ich 
sonst falsche Ergebnisse bekomme. Das habe ich bereits in meinem Excel 
ausführlich getestet. Wenn ich die Grenzfrequenz näher an die 
Samplefrequenz heranschiebe oder die Samplefrequenz verringere, 
verschwinden die Störsignale nicht so vollständig wie gewünscht.

@eProfi:

1. und 4. Es dreht einen Motor. Der Motor dreht eine Scheibe. Auf der 
Rückseite der Scheibe sind schwarze und weiße Streifen. Diese Streifen 
schaut sich ein Sensor an, während sie vorbeiziehen. Es sind ca 1 bis 
maximal 3 schwarze Streifen pro Sekunde. Nach sieben Streifen (ungefähr 
420° Drehung) soll der Motor stoppen und fünf Minuten lang ruhig sein.

Ein LDR war mein erster Ansatz, der war aber zu langsam. (ich habe es 
erst nicht geglaubt, aber es ist so.)
Also wurde es eine Fotodiode. Die Fotodiode ist schnell genug. Die 
schaut sich natürlich auch die 50Hz aus der Energiesparlampe, die 150Hz 
aus meiner Osram Neonröhre und die Schaltungsinternen Störfrequenzen aus 
dem Motor mit an. Deshalb soll vernünftig gefiltert werden. Ein 
Einfachst-Filter (gleitender Durchschnitt oder sowas) kann das NICHT. Da 
erscheinen die Störfrequenzen an der Abtastrate gespiegelt 
unvorhersehbar irgendwo - falls die Abtastrate in der Nähe der 
Störfrequenz liegt. Bei dieser Anwendung wäre das leiter nötig. Daher 
baue ich lieber gleich einen echten Filter. Von der Geschwindigkeit und 
Rechenkapazität und Speicherkapazität kann der Attiny85 das ganz locker. 
Klar soweit?

Die Filterauslegung braucht man heutzutage nicht mehr rechnen, das 
machen online Tools wie dieses hier: 
http://www.dsptutor.freeuk.com/IIRFilterDesign/IIR... ganz 
kostenlos und einfach für dich. Deshalb ist das auch überhaupt kein 
großer Aufwand - Rechenkapazität braucht es vergleichbar viel wie ein 
guter gleitender Durchschnitt, es funktioniert nur viel besser.

2. Ich bin der Meinung dass es mit einigen Subfunktionen und viel 
Kommentar später leichter für mich verständlich ist. Ich verwende meine 
Subs in späteren Programmen wieder, und da ist es gut wenn sie von 
vornherein freigeschnippelt und kommentiert sind. Der Compiler löscht 
den überflüssigen Kram schließlich, der AVR kriegt davon nicht viel mit.

3. Klar, das ist sinnvoll Zweierpotenzen zu nehmen. Das setze ich um. 
Danke.

Autor: Andreas Wagener (calidus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe den möglichen Fehler gefunden, ich komme aber nicht darauf 
weshalb das so ist.

Ich habe herausgefunden:
Er verwendet die beiden ISRs.
Er geht in die States
- Weiterdrehen
- Messen_starten
- Warten_auf_Messung

Was er anscheinend nicht tut:

Obwohl er in die ISR vom ADC geht, geht er hinterher nicht in den State 
Messen_fertig.

Hat einer eine Ahnung warum?
Habe ich den Interrupt falsch programmiert? Hier noch mal in kürze der C 
Code von der ISR
//Der ADC meldet sich hier, wenn die Messung fertig ist.
ISR (ADC_vect)
{
  communication = MESSEN_FERTIG;
  sei();
}

Autor: EM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Muss das ADC Int Flag gelöscht werden, oder wird das automatisch bei 
Auslösen der Int Prozedur erledigt? Hab jetzt gerade kein Datenblatt zur 
Hand.

Grüße

Autor: EM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und setz mal communication auf volatile, damit der Compiler da 
"Bescheid" weiß.

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
GCC (und die anderen C Compiler wohl auch) kümmert sich schon selbst um 
das retten des SREG, und das SEI am Ende der ISR  ist auch nicht nötig 
aber eventuell gefährlich.

Das Interrupt-flag  vom AD wird schon beim Aufruf der ISR vom µC 
gelöscht.

Autor: Andreas Wagener (calidus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Entweder war es der Volatile oder der Sei()...

jedenfalls geht es jetzt. Vielen Dank euch allen!

Autor: Christian Berger (casandro) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kleiner Tipp übrigens, setzte die Abtastrate auf ein größeres Vielfaches 
der Störfrequenz. Dann falten sich die Störungen tendenziell eher auf 
höhere Frequenzen. Davor muss aber ein Filter rein.

Autor: Andreas Wagener (calidus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke!
Es funktioniert inzwischen.

Youtube-Video "Farbendreher"

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.