www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PWM einlesen funkltioniert nicht richtig - Fehler?!


Autor: Daniel B. (scheinleistung)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich lese das erste Mal mit dem ATMEGA16 ein Servosignal ein. Folgenden 
Code habe ich mir überlegt. Beim Test sieht es leider so aus dass es 
nicht funktioniert, die betreffende LED die zum Test eingeschalten 
werden soll wenn das Signal >1,5ms lang ist blinkt in einem unruhigen 
Rhytmus...
Anbei ein Bild wie ich das Signal eines Servotesters einspeise an den 
PIN PD6 (ICP). Kann mir jemand einen Tipp geben was der Fehler sien 
könnte und hat mir jemand ein Feedback zu meiner unten stehenden Lösung?


Einstellen des Timers:

  // Rauschunterdrückung (Bit7) und steigende Flanke (Bit6) erkennen und CPU-Takt / 8 (enstspr 0,57us)
  // d.h. Zählregister TCNT1 inkrementiert mit dieser Periode
  TCCR1B  = 0b11000010;

  // Interrupt für ICP (Input Capture Pin) einschalten
  TIMSK  |=(1<<TICIE1);

Die Interrupt Routine:
extern int startWert;
extern int endWert;
extern double servoSignal;


// Timer 1 Capture Event Interrupt wenn Flanke an ICP Pin (PD6) --> Servosignal einlesen!
ISR(TIMER1_CAPT_vect)
{  
  // Wenn STEIGENDE Flanke erkannt werden soll, startwert einlesen (akt. Zählerstand)
  if(TCCR1B == 0b11000010)
  {  
    cli();
    startWert = ICR1L + 16*ICR1H;  // einlesen mit deaktivierten Interrupts
    sei();

    TCCR1B  = 0b10000010;      // auf fallende Flanke umstellen!
  }

  // Wenn FALLENDE Flanke erkannt werden soll, endwert einlesen (akt. Zählerstand)
  else if(TCCR1B == 0b10000010)
  {
    cli();
    endWert = ICR1L + 16*ICR1H;    // einlesen mit deaktivierten Interrupts
    sei();

    if (startWert < endWert)
    {
      servoSignal = (endWert - startWert) * 5.7413e-7;
    }

    else
    {
      servoSignal = (endWert - startWert + 65536) * 5.7413e-7;
    }

    // Zähler zurück setzen
    cli();
    TCNT1H = 0b00000000;
    TCNT1L = 0b00000000;
    sei();

    TCCR1B  = 0b11000010;      // wieder auf steigende Flanke umstellen!
          
  }


}


Aufruf in der MAIN:
    if(servoSignal >= 0.0015)
    {
      LIGHTPORT1  = 0b11111110;
    }

    else if(servoSignal < 0.0015)
    {
      LIGHTPORT1  = 0b11111101;
    }

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lesen: AVR 16-Bit-Register
Und es gibt keinen vernünftigen Grund, Steuerregister wie das TCCR1B in 
Binärschreibweise zu setzen. Benutze auch dort die Schreibweise mit den 
Bitnamen!

Außerdem ist in einem Interrupt Handler die Bearbeitung anderer 
Interrupts automatisch durch die Hardware gesperrt. cli() und sei() 
haben im Interrupt Handler nichts zu suchen. Das sei() gibt an der 
Stelle nämlich die vorher gesperrten Interrupts wieder frei.

Bitte auch im AVR-GCC-Tutorial die entsprechenden Abschnitte lesen. 
Außerdem glaube ich nicht, dass da wirklich Gleitkommazahlen nötig sind. 
Festkommaarithmetik heißt hier das Zauberwort.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> servoSignal = (endWert - startWert + 65536) * 5.7413e-7;
Hier wird z.B. erstmal "endWert - startWert + 65536" in int berechnet. 
Der Wertebereich von int ist aber nur -32768...32767, das läuft über. 
Außerdem sind langwierige Gleitkommaoperationen (so sie denn überhaupt 
sinnvoll sind) in Interrupt-Handlern völlig fehl am Platz, ganz 
besonders, wenn es zeitkritisch ist.

Autor: Daniel B. (scheinleistung)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sind eigentlich long int ! war bei extern falsch angegeben!

Ich werde die vorgeschlagenen Änderungen mal testen, danke!

Wie würde man denn dann am besten aus dem Zählerwert den Zeitwert 
berechnen, oder soll ich am besten gar keine Zeitwerte benutzen sondern 
ermitteln welche Zählerwerte meinen gewünschten Zeiten entsprechen und 
dann nur noch mit Zählwerten arbeiten?

Autor: Daniel B. (scheinleistung)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Johannes M. das war der Fehler - wieder was gelernt!

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
<korinthenkack>
> Hier wird z.B. erstmal "endWert - startWert + 65536" in int berechnet.
Das stimmt nicht ganz:
 Wenn "endWert" und "startWert" vom Typ int sind wird die Subtraktion 
als signed int , aber die Addition als signed long gerechnet, weil 
65536 (0x10000) nicht mehr in ein int passt. Das kann man mit 32767 
(0x7fff) und 32768 (0x8000) schön ausprobieren ("integer promotion" als 
Such-/Stichwort).

hth, Jörg

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.