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


von Daniel B. (scheinleistung)


Angehängte Dateien:

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:
1
  // Rauschunterdrückung (Bit7) und steigende Flanke (Bit6) erkennen und CPU-Takt / 8 (enstspr 0,57us)
2
  // d.h. Zählregister TCNT1 inkrementiert mit dieser Periode
3
  TCCR1B  = 0b11000010;
4
5
  // Interrupt für ICP (Input Capture Pin) einschalten
6
  TIMSK  |=(1<<TICIE1);

Die Interrupt Routine:
1
extern int startWert;
2
extern int endWert;
3
extern double servoSignal;
4
5
6
// Timer 1 Capture Event Interrupt wenn Flanke an ICP Pin (PD6) --> Servosignal einlesen!
7
ISR(TIMER1_CAPT_vect)
8
{  
9
  // Wenn STEIGENDE Flanke erkannt werden soll, startwert einlesen (akt. Zählerstand)
10
  if(TCCR1B == 0b11000010)
11
  {  
12
    cli();
13
    startWert = ICR1L + 16*ICR1H;  // einlesen mit deaktivierten Interrupts
14
    sei();
15
16
    TCCR1B  = 0b10000010;      // auf fallende Flanke umstellen!
17
  }
18
19
  // Wenn FALLENDE Flanke erkannt werden soll, endwert einlesen (akt. Zählerstand)
20
  else if(TCCR1B == 0b10000010)
21
  {
22
    cli();
23
    endWert = ICR1L + 16*ICR1H;    // einlesen mit deaktivierten Interrupts
24
    sei();
25
26
    if (startWert < endWert)
27
    {
28
      servoSignal = (endWert - startWert) * 5.7413e-7;
29
    }
30
31
    else
32
    {
33
      servoSignal = (endWert - startWert + 65536) * 5.7413e-7;
34
    }
35
36
    // Zähler zurück setzen
37
    cli();
38
    TCNT1H = 0b00000000;
39
    TCNT1L = 0b00000000;
40
    sei();
41
42
    TCCR1B  = 0b11000010;      // wieder auf steigende Flanke umstellen!
43
          
44
  }
45
46
47
}

Aufruf in der MAIN:
1
    if(servoSignal >= 0.0015)
2
    {
3
      LIGHTPORT1  = 0b11111110;
4
    }
5
6
    else if(servoSignal < 0.0015)
7
    {
8
      LIGHTPORT1  = 0b11111101;
9
    }

von Johannes M. (johnny-m)


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.

von Johannes M. (johnny-m)


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.

von Daniel B. (scheinleistung)


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?

von Daniel B. (scheinleistung)


Lesenswert?

Danke Johannes M. das war der Fehler - wieder was gelernt!

von Jörg G. (joergderxte)


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

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.