PWM einlesen funkltioniert nicht richtig - Fehler?!

OP #1232779
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
    }
Angehängte Dateien:
#1232782
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.
#1232789
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.
OP #1232800
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?
#1232836
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren