Forum: Mikrocontroller und Digitale Elektronik AVR: Problem mit Interrupt bei RC Car


von Tim (Gast)


Angehängte Dateien:

Lesenswert?

Hallo liebes Forum,

ich wende mich an euch, weil ich nicht mehr weiter weiß.
Ich verwende in meinem ferngesteuerten Auto einen ATMEGA16, dessen 
Interrupt INT0 das PWM-Signal (THRO) vom Fahrtenregler abgreift und 
dessen Interrupt INT1 ein weiteres PWM-Signal (ELEV) vom einem anderen 
Kanal abgreift. Mit dem Motorsignal steuer ich Bremslicht und 
Rückfahrlicht. Mit dem anderen Kanal möchte ich Warnblinker und 
Blaulicht steuern.
Ich prüfe in den Interrupt Routinen, die auf steigende Flanken 
reagieren, ob der Impuls länger oder kürzer als die 1,5ms in 
Hebel-Mittelstellung ist.
Das ganze klappt soweit auch, es gibt nur ein Problem: Manchmal, geht 
mein Warnblinker an, wenn ich am Vor-Zurück-Hebel (also INT0) spiele. 
Das heißt, dass INT0 irgendeinen Einfluss auf INT1 hat. Das kann aber 
irgendwie nicht sein. Das ganze hat bereits wunderbar mit einem ATMEGA8 
geklappt.

Anbei meine Interrupt Routinen.

Manchmal wird also warnblinker_an = 1 gesetzt, obwohl nur in INT0 etwas 
geschehen soll. Ich weiß übrigens, dass das messen der Impulse mit 
Delays nicht furchtbar professionell ist, aber ich scheue etwas die 
Verwendung von Timern.

Wäre klasse wenn Ihr helfen könntet.

Gruß

von pst (Gast)


Lesenswert?

Mit den Delays stoeren sich deine beiden Interrupt-Routinen gegenseitig. 
So lange der eine wartet, wird auch der andere verzoegert, das bringt 
die ganze Messung durcheinander. Du solltest die Signallaengen mit 
Timern messen und an deren Ende auswerten.

von Tim (Gast)


Lesenswert?

Vielen Dank für den Hinweis. Ich versuche es jetzt mit Timern:

1
...
2
// INT1 aus steigende Flanke sensitiv
3
MCUCR = (1 << ISC11) | (1 << ISC10);
4
...
5
// Interrupt Routine für ELEV an INT1: Blaulicht und Warnblinker
6
ISR(INT1_vect)
7
{  
8
  if( timer0_started == 0 )
9
  {
10
    // Dafür sorgen, dass INT1 nun auf fallende Flanke reagiert
11
    // Löschen eines Interrupt Flags mit schreiben einer 1!!!
12
    MCUCR = (1 << ISC10);
13
    timer0_started = 1;
14
    TCNT0 = 0;
15
    // CPU Takt: 1 MHz -> Prescaler: 8 -> Imkrement dauert 8 us
16
    TCCR0 = (1 << CS01);
17
  }
18
  if( timer0_started == 1 )
19
  {
20
    // Dafür sorgen, dass INT1 wieder auf steigende Flanke reagiert
21
    MCUCR = (1 << ISC10);  
22
    timer0_started = 0;  
23
    
24
    // 1,4 ms entspricht Zählerstand von 175: 1400 us / 8 us = 175
25
    if( TCNT0 < 175 )
26
    {
27
      if( warnblinker_an == 0 ) warnblinker_an = 1;
28
      else warnblinker_an = 0;
29
    }
30
    
31
    // 1,6 ms entspricht Zählerstand von 200: 1600 us / 8 us = 200
32
    if ( TCNT0 > 200)
33
    {
34
      if( blaulicht_an==0 ) blaulicht_an = 1;
35
      else blaulicht_an = 0;    
36
    }     
37
  }  
38
}

Nun geht mein Warnblinker die ganze Zeit ohne das etwas passiert wenn 
ich den Hebel an der Fernsteuerung bewege. Wo ist mein Fehler?

von Tim (Gast)


Lesenswert?

Ich habe mal aus dem
1
  if( timer0_started == 1 )
ein
1
  else if( timer0_started == 1 )
gemacht, damit ich mir den Timer nicht direkt wieder ausmache. Schön 
doof...
Aber es klappt immer noch nicht so recht.

von OldMen (Gast)


Lesenswert?

Du hast die Timer und deren Möglichkeiten nicht wirklich verstanden.
Dein Ansatz den Du hier zeigst, ist nicht durchdacht.
Lies mal hier die Tutorials und schreib Dir mal erst einmal ein kleines 
Programm das nur eine LED blinkden lässt. So hat, glaube ich, hier jeder 
mal angefangen.
Wenn das läuft, dann solltest Du dir die Möglichkeiten anschauen die 
Timer, jeder AVR hat mindestens zwei davon, bieten.
Sprich: Timer0 für Aufgabe A und Timer1 für Aufgabe B. Der CTC-Mode ist 
für Deine Zwecke Ideal! Bspw. für den Warnblinker.
Nicht alles in EINE Funktion stecken. Da blickst Du irgendwann nicht 
mehr durch und suchst nur Fehler.
Ausserdem sollte man ISRs nicht mir Code überladen, sonst können evt. 
nachfolgende Interrupts verloren gehen.
In diesem Sinne, viel Erfolg.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

OldMen schrieb:
> Ausserdem sollte man ISRs nicht mir Code überladen, sonst können evt.
> nachfolgende Interrupts verloren gehen.
Ich würde im Interrupt nur einen Zeitstempel nehmen, und den in der 
Hauptschleife auswerten. Lustigerweise kann das ein uC sogar in der 
Hardware: Timer Capture heißt diese Funktion, wo ein Pegelwechsel an 
einem Pin einen Zeitstempel in ein Register ablegt.
Hier mal ein paar Worte dazu:
http://www.mikrocontroller.net/articles/High-Speed_capture_mit_ATmega_Timer

: Bearbeitet durch Moderator
von Tim (Gast)


Lesenswert?

> Du hast die Timer und deren Möglichkeiten nicht wirklich verstanden.
> Dein Ansatz den Du hier zeigst, ist nicht durchdacht.


Also das verstehe ich nicht...
Ich resete den Timer, ich mache ihn an und und lese ihn aus. Wo ist denn 
da der Fehler? Stimmt denn meine Berechnung mit dem Prescaler?

von OldMen (Gast)


Lesenswert?

Tim schrieb:
> ich mache ihn an und und lese ihn aus

Genau das meinte ich mit:

OldMen schrieb:
> Du hast die Timer und deren Möglichkeiten nicht wirklich verstanden.

von Tim (Gast)


Lesenswert?

OK, ich arbeite das Tutorial nochmal durch und probiere es mit einer 
LED. Ich melde mich dann wieder.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Tim schrieb:
> Ich resete den Timer, ich mache ihn an und und lese ihn aus. Wo ist denn
> da der Fehler?
Bitte ankreuzen:
Du hast meinen Post
[ ] nicht gesehen.
[ ] nicht verstanden.

Kurz: mach die Interruptroutine so kurz wie möglich und den Rest der 
Arbeit in der Hauptschleife. Wenn möglich (und bei dir ist das der Fall) 
verwende Hardwarekomponenten des uC zur Lösung deines Problems.

von Tim (Gast)


Lesenswert?

Lothar Miller schrieb:
> Bitte ankreuzen:
> Du hast meinen Post
> [ ] nicht gesehen.
> [ ] nicht verstanden.

Danke, ich habe deinen Post gelesen. Ich muss drei PWM Signale messen 
und das mit einem ATMEGA16. Da dieser nur einen ICP Eingang hat, spricht 
doch nichts dagegen 3 Timer anzuwerfen und auszulesen, oder?

Anbei:
[x] habe gelesen, dass ISRs kurz sein sollen
[ ] habe nicht gelesen, dass ISRs kurz sein sollen

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

Tim schrieb:
> spricht doch nichts dagegen 3 Timer anzuwerfen und auszulesen, oder?
Wenn du drei Eier kochst, dann nimmst du ja auch nicht 3 Uhren, oder? 
Nein, du merkst dir, wann du die braunen Eier reingetan hast, und wann 
die weißen...

Ich könnte die Aufgabe mit 1 durchlaufenden Timer und 3 Subtraktionen 
auch. Warum muss der Timer auf 0 gesetzt werden? Gefährlich ist bei 
einem durchlaufenden Timer ist nur der Überlauf. (Siehe dazu das Bild) 
Das ist hier aber ganz einfach:
1
Interrupt -->
2
   Steigende Flanke --> Timer im TimerwertA merken
3
   Fallende Flanke -->  Timer im TimerwertB merken  
4
5
Hauptschleife -->
6
Wenn TimerwertA < TimerwertB dann
7
  Pulsdauer = TimerwertB - TimerwertA
8
  Wenn Pulsdauer  zu groß --> Überlauf, nichts tun.
9
  Wenn Pulsdauer  >1,5ms  --> Lampe an
10
  Sonst                   --> Lampe aus

Mit dem selben Timer könntest du jetzt auch noch die anderen beiden Pins 
bedienen.

: Bearbeitet durch Moderator
von Tim (Gast)


Lesenswert?

OK, gute Lösung. Ich wollte mit meinem Kommentar nur begründen, wieso 
ich nicht den ICP verwenden kann. Trotzdem würde mich interessieren, was 
an meinem Code falsch ist, denn es klappt ja nicht. Wir sprechen ja 
gerade nur über optimiertere Lösungen, die ich jetzt auch umsetze.

Stimmt meine Berechnung mit dem Prescaler?
1
    // CPU Takt: 1 MHz -> Prescaler: 8 -> Imkrement dauert 8 us


Würdest du zum Beispiel immer die Sensitivität des Interrupts ändern, 
oder einfach auf jeden Pegelwechsel reagieren? Ich habe Angst, dass der 
uC quasi mit einer fallenden Flanke anfängt (->TimerwertA) anstatt mit 
einer steigenden.

Bei deiner Lösung könnte doch während des Pulses ein Überlauf auftreten 
und somit ist TimerwertB < TimerwertA. Was dann? So lange warten bis ich 
einen Puls erwische bei dem das nicht passiert? Schlecht ist es ja 
allgemein, wenn zwischen TimerwertA und TimerwertB ein Überlauf 
auftritt.

von Sefco (Gast)


Lesenswert?

Guten Abend,

ich habe mich jetzt nochmal von unten rangearbeitet.
Mein aktueller Code tut folgendes (oder soll es tuen):
INT0 reagiert zunächst auf steigende Flanken. Kommt ein Empfängerimpuls, 
so wird INT0 auf fallende Flanke umkonfiguriert, der Timer zurückgesetzt 
und gestartet mit dem Prescaler 256 (mein Takt ist 8 MHz).
Hört mein PWM Signal auf (fallende Flanke), so wird der Timer gestoppt, 
der Interrupt INT0 wieder auf steigende Flanke umkonfiguriert und der 
Wert des Timers auf < 40 oder > 53 geprüft.
Das Ganze sieht so aus:
1
...
2
  MCUCR = (1 << ISC01)|(1 << ISC00);
3
...
4
ISR(INT0_vect)
5
{  
6
  if ( (MCUCR & (1<<ISC00)) )
7
  {
8
    // Wird nur bei steigender Flanke ausgeführt
9
    
10
    // INT0 auf fallende Flanke einstellen
11
    MCUCR &= ~(1 << ISC00);
12
    
13
    // Timer zurücksetzen
14
    TCNT0 = 0;
15
    // Prescaler 256 
16
    TCCR0 = (1<<CS02);    
17
  }
18
  else if ( !(MCUCR & (1<<ISC00)) )
19
  {
20
    // Wird nur bei fallender Flanke ausgeführt
21
    
22
    // INT1 auf steigende Flanke einstellen
23
    MCUCR |= (1 << ISC00);
24
    
25
    //Timer stoppen
26
    TCCR0 &= ~(1<<CS02);
27
    if (TCNT0 > 53)
28
    {
29
      // Licht an, wenn Hebel nach unten ausgelenkt
30
      PORTD |= (1<<ABBLENDLICHT_VORNE);
31
    }
32
    if (TCNT0 < 40)
33
    {
34
      // Licht aus, wenn Hebel nach oben ausgelenkt
35
      PORTD &= ~(1<<ABBLENDLICHT_VORNE);
36
    }    
37
  }
38
}

Das PWM Signal eines RC Empfängers an ein Servo ist bei unausgesteuertem 
Hebel an der Fernsteuerung 1,5ms lang. Bei voller Aussteuerung 1ms bzw. 
2ms. Wenn der Timer mit 8MHz/256 = 32,25 kHz zählt, braucht der für das 
Erhöhen im TCNT0 1 / 32,25 kHz = 32 us. Der Timer muss bei einem Impuls 
von 1ms also den Wert 1000 us / 32 us = 31 und bei 2ms den Wert 2000 us 
/ 32 us = 62 haben. Ich prüfe einfach mal den Timerwert für <40 und >53.

Nun zur Reaktion meiner LED: Sie macht etwas, aber recht komisch. Mal 
geht sie an und aus, wenn ich den Hebel nur nach oben bewege und 
umgekehrt.

Wo ist jetzt mein Fehler? Ich habe mich Stück für Stück rangetastet und 
z.B. erstmal geprüft, ob das umkonfigurieren der Sensitivität des 
Interrupts funktioniert etc. Wenn ich den Overflow INT anschalte und 
eine LED dort Toggle, so springt diese bei Aussteuerung des Hebels an. 
Nur wie kann da ein Overflow auftreten? Stimmt was an meinen 
Berechnungen nicht oder sieht das PWM Signal anders aus als üblich (habe 
kein Oszi um das zu prüfen).

Vielen Dank & Gruß

von Sefco (Gast)


Lesenswert?

Entschuldigung für den falschen Namen!

Sefco = Tim

von Karl H. (kbuchegg)


Lesenswert?

> der Timer zurückgesetzt und gestartet mit dem Prescaler 256
>
tu dir selbst einen Gefallen und vergiss endlich dieses "Timer stoppen, 
Timer starten, Timer zurücksetzen".

Lass den Timer durchlaufen.
Man kann mit einer normalen Armbanduhr mit Sekundenzeiger auch Zeiten 
stoppen! Man muss sich nur merken, bei welchem Sekundenstand der Anfang 
eines Ereignisses war und bei welchem Sekundenstand das Ende eintrat. 
Die Differenz der beiden sagt dir dann wie lange das Ereignis dauerte.
Aber: Das kannst du mit beliebig vielen Ereignissen machen! Obwohl du 
nur eine einzige Uhr hast. WEnn du aber dauernd am Sekundzeiger 
rumfummelst, dann wird das nichts mit "mit lediglich einer Uhr mehrere 
ereignisse austimen"

In deinem Fall ist das sogar besonders einfach. Denn wenn du deinen 
Startzählerstand und Endzählerstand als unsigned int ausführst(*) und 
ganz einfach die Differenz davon nimmst, dann brauchst du noch nicht 
einmal berücksichtigen, dass es einen Timeroverflow gab (wenn der Timer 
so eingestellt ist, dass diese Differenz prinzipiell nicht größer als 
65535 werden kann).

Der Timer läuft durch und zählt vor sich hin.
In jedem Interrupt unterscheidest du
* war es eine steigende Flanke?
  in diesem Fall merkst du dir den Zählerstand und konfigurierst den 
Interrupt um auf fallende Flanke
* war es aber eine fallende Flanke, dann rechnest du die Differenz 
zwischen dem jetzt gültigen Zählerstand und dem gemerkten Startwert aus. 
Diese Differenz ist der WErt, der dich interessiert. Den Interrupt 
konfigurierst du noch auf steigend und fertig ist die Schose.

In der Hauptschleife wertest du dann die ermittelten Differenzen aus. 
Oder aber, wenn es dabei bleibt und das ganze so kurz wie hier ist
1
    if (TCNT0 > 53)
2
    {
3
      // Licht an, wenn Hebel nach unten ausgelenkt
4
      PORTD |= (1<<ABBLENDLICHT_VORNE);
5
    }
6
    if (TCNT0 < 40)
7
    {
8
      // Licht aus, wenn Hebel nach oben ausgelenkt
9
      PORTD &= ~(1<<ABBLENDLICHT_VORNE);
10
    }
dann eben in der ISR. Aber mit der ermittelten Differenz arbeiten! TCNT 
interessiert dich nur zum ermitteln der Differenz mit dem TCNT als die 
steigende Flanke kam.


(*) in diesem Fall, wenn die Werte sowieso nicht größer als 255 werden 
können, dann eben einen uint8_t. Also alles in 8 Bit und nicht in 16.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Sowas
1
  if ( (MCUCR & (1<<ISC00)) )
2
  {
3
...
4
  }
5
  else if ( !(MCUCR & (1<<ISC00)) )

kannst du dir sparen.
Ein Bit kann nur entweder 0 oder 1 sein. Wenn es 1 ist, dann kann es 
nicht gleichzeitig 0 sein. Das muss man daher nicht testen.
1
  if ( (MCUCR & (1<<ISC00)) )
2
  {
3
   ...
4
  }
5
  else
6
  {
7
   ...
8
  }

Die Verneinung in einem weiteren else if anzuführen ist nur eine weitere 
Möglichkeit Fehler zu machen. Praktischen Zweck oder Sinn hat sie 
keinen.

von Karl H. (kbuchegg)


Lesenswert?

Ich hab jetzt deine Berechnungen nicht nachgerechnet

> Stimmt was an meinen Berechnungen nicht oder sieht das PWM Signal anders aus als 
üblich (habe kein Oszi um das zu prüfen).

Häng ein normales Servo an. Wenn es sich normal verhält, dann ist das 
Servosignal auch ein ganz normales.

von Sefco (Gast)


Lesenswert?

Karl Heinz schrieb:
> tu dir selbst einen Gefallen und vergiss endlich dieses "Timer stoppen,
> Timer starten, Timer zurücksetzen".


Was passiert, wenn während der Impulsdauer ein Overflow auftritt? Dann 
ist meine Differenz unbrauchbar, oder etwa nicht? Ich kann das gerne 
später, wenn ich mehrere Pulse messen will, so machen. Aber es klappt 
nicht mal die einfache und gut leserliche Version mit dem Zurücksetzten 
des Timers?!?!

Im Übrigen wurde meine Frage nicht beantwortet. Wieso klappt es nicht? 
Wieso macht meine LED zwar etwas (z.B. nur an gehen, aber nicht mehr 
ausgehen), wenn ich den Hebel auslenke, aber nicht das, was ich gerne 
hätte. Habe ich den Prescaler falsch eingestellt? Wie kann Überhaupt bei 
den Einstellungen ein Overflow auftreten, wenn bei 2ms max. Impulsbreite 
der Zähler max. 63 sein kann?


Karl Heinz schrieb:
> Häng ein normales Servo an. Wenn es sich normal verhält, dann ist das
> Servosignal auch ein ganz normales.

Ja es verhält sich ganz normal.

von Stefanus (Gast)


Lesenswert?

> Was passiert, wenn während der Impulsdauer ein Overflow auftritt?
> Dann ist meine Differenz unbrauchbar, oder etwa nicht?

Lies noch mal den Beitrag von Karl Heinz.

von Sefco (Gast)


Lesenswert?

Karl Heinz schrieb:
> In deinem Fall ist das sogar besonders einfach. Denn wenn du deinen
> Startzählerstand und Endzählerstand als unsigned int ausführst(*) und
> ganz einfach die Differenz davon nimmst, dann brauchst du noch nicht
> einmal berücksichtigen, dass es einen Timeroverflow gab (wenn der Timer
> so eingestellt ist, dass diese Differenz prinzipiell nicht größer als
> 65535 werden kann).
>
> Der Timer läuft durch und zählt vor sich hin.
> In jedem Interrupt unterscheidest du
> * war es eine steigende Flanke?
>   in diesem Fall merkst du dir den Zählerstand und konfigurierst den
> Interrupt um auf fallende Flanke
> * war es aber eine fallende Flanke, dann rechnest du die Differenz
> zwischen dem jetzt gültigen Zählerstand und dem gemerkten Startwert aus.
> Diese Differenz ist der WErt, der dich interessiert. Den Interrupt
> konfigurierst du noch auf steigend und fertig ist die Schose.


Also das verstehe ich überhaupt nicht. Wie soll in bei einem 8 Bit Timer 
denn den Zählerstand als unsigned int ausführen? Ich kann von 0 bis 255 
Zählen und nicht weiter.
Ich bin weiterhin der Ansicht, dass meine Lösung vielleicht nicht die 
Beste ist, aber funktionieren müsste.
Könnte ihr mir nicht hierzu was sagen?

Sefco schrieb:
> Habe ich den Prescaler falsch eingestellt? Wie kann Überhaupt bei
> den Einstellungen ein Overflow auftreten, wenn bei 2ms max. Impulsbreite
> der Zähler max. 63 sein kann?

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> Also das verstehe ich überhaupt nicht. Wie soll in bei einem 8 Bit Timer
> denn den Zählerstand als unsigned int ausführen?

Ich hab noch einen Nachtrag im Beitrag gemacht.
Bei einem 8-Bit Timer natürlich dann ein uint8_t.

Ich bin irgendwie davon ausgegangen, dass du den 16 Bit Timer 1 benutzen 
wirst.

> Ich kann von 0 bis 255
> Zählen und nicht weiter.

Ja und?
255 + 1 -> 0

Das ändert nichts daran, dass auch dann die unsigned Rechnerei immer das 
richtige Ergebnis bringt.
Jaaaa. 3 - 254 bringt (unsigned gerechnet) das Ergebnis 5 (es gibt ja 
keine negative Zahlen - unsigned!). Und genau soviele Takte sind von 254 
bis zum Zählerstand 3 vergangen.

254
255
0
1
2
3

> Ich bin weiterhin der Ansicht, dass meine Lösung vielleicht nicht die
> Beste ist, aber funktionieren müsste.
> Könnte ihr mir nicht hierzu was sagen?

Nach dem Umschalten der Flankenrichtung immer auch noch das zum 
Interrupt gehörende Interrupt Flag löschen. Es kann sein, dass alleine 
durch den Vorgang des Umschaltens von der Hardware ein falscher 
Interrupt registriert wird. Der muss gelöscht werden, sonst stimmen 
logischerweise die ermittelten Zeiten nicht.

>> Habe ich den Prescaler falsch eingestellt? Wie kann Überhaupt bei
>> den Einstellungen ein Overflow auftreten, wenn bei 2ms max. Impulsbreite
>> der Zähler max. 63 sein kann?

Nochmal.
Lass den verdammten Timer einfach laufen.
Diese Start/Stop/Reset Technik ist scheisse. Die braucht kein Mensch. Du 
verbaust dir damit nur die Möglichkeit, den Timer auch noch für etwas 
anderes zu benutzen als nur einen einzigen Servopuls auszumessen. So 
gesehen wirfst du dir also eigentlich Prügel zwischen die Beine. Völlig 
unnötiger Weise.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

1
ISR(INT0_vect)
2
{  
3
  static uint8_t startTime;
4
5
  if ( (MCUCR & (1<<ISC00)) )
6
  {
7
    startTime = TCNT0;
8
    // INT0 auf fallende Flanke einstellen
9
    MCUCR &= ~(1 << ISC00);
10
  }
11
  else
12
  {
13
    uint8_t length = TCNT0 - startTime;
14
15
    // INT1 auf steigende Flanke einstellen
16
    MCUCR |= (1 << ISC00);
17
18
    if (length > 53)
19
    {
20
      PORTD |= (1<<ABBLENDLICHT_VORNE);
21
    }
22
    if (length < 40)
23
    {
24
      PORTD &= ~(1<<ABBLENDLICHT_VORNE);
25
    }    
26
  }
27
28
  GIFR |= ( 1 << INTF0 );
29
}

probier das mal.

: Bearbeitet durch User
von Sefco (Gast)


Lesenswert?

Karl Heinz schrieb:
> Lass den verdammten Timer einfach laufen.
> Diese Start/Stop/Reset Technik ist scheisse. Die braucht kein Mensch. Du
> verbaust dir damit nur die Möglichkeit, den Timer auch noch für etwas
> anderes zu benutzen als nur einen einzigen Servopuls auszumessen. So
> gesehen wirfst du dir also eigentlich Prügel zwischen die Beine. Völlig
> unnötiger Weise.

Beispiel:
Puls ist 100 lang.
Nutze 8 Bit Timer.


Puls ist Low und Timer läuft.
Puls geht auf High -> Nehme Zeitstempel: stamp1 = aktueller Timerwert = 
z.B. = 200
Es tritt ein Overflow auf.
Puls geht auf Low nach 100 -> Nehme Zeitstempel: stamp2 = aktueller 
Timerwert = 45

Werte in der main() aus:

if( stamp2 - stamp1 < oder > xxx) mache etwas.
--> Klappt nicht, weil 45 - 200 != 100. Kann also bei Overflow nicht den 
Puls erfassen, wenn der Timer die ganze Zeit läuft und nicht resetet 
wird.

Wie soll das bei einem Overflow klappen?

von Sefco (Gast)


Lesenswert?

Oh du hast einen Code gepostet :) Moment ich teste!

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> --> Klappt nicht, weil 45 - 200 != 100.

Sagt wer?(*)

> Wie soll das bei einem Overflow klappen?

Wie oft soll ich mir eigentlich noch den Mund fusselig reden?
Die Differenz zweier unsigned Werte liefert als unsigned Operation den 
korrekten Wert. Selbst dann wenn da ein Overflow dazwischen liegt.
Soll ich es dir auf Bitebene vorrechnen?

(*) ok, da kommt nicht 100 raus, sondern 101.
Das liegt aber daran, dass 200 + 100 auch nicht 45 ergibt, sondern 44

44 - 200 ergibt aber wieder die gesuchten 100

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> Werte in der main() aus:
>
> if( stamp2 - stamp1 < oder > xxx) mache etwas.

Das habe ich allerdings so nicht geschrieben.
Du sollst die Differenz ausrechnen und dafür sorgen, dass da wieder (in 
deinem Fall) ein uint8_t rauskommt. Im einfachsten Fall
1
  uint8_t start = 200;
2
  uint8_t end = 45;
3
4
  uint8_t diff = end - start;
5
6
  if( diff == 101 )
7
  {
8
    ...
9
  }

und das klappt!

Denn wenn du das so schreibst
1
   if( end - start < 54 )
passiert durch die C Regeln ein bischen etwas anderes. Speziell ist das 
dann kein 8 Bit Vergleich mehr, sondern ein 16 Bit Vergleich und das 
ganze findet als int Aktion statt. Und genau das wollen wir nicht. Wir 
wollen auf jeden Fall die ganze Operation als 8 Bit unsigned Operation 
abwickeln.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:
> Oh du hast einen Code gepostet :) Moment ich teste!

Und?
Wars das Löschen des INTF0 Flags?

Ich denke "Ja". Aber da ich das hier nicht laufen habe, bin ich mir auch 
nicht 100% sicher. Auf jeden Fall fällt mir momentan keine andere 
Fehlermöglichkeit mehr ein.

Ausser:
* die Taktfrequenz stimmt nicht
* die ISR wird gar nicht aufgerufen

: Bearbeitet durch User
von Sefco (Gast)


Lesenswert?

Also erstmal möchte ich dir herzlich danken, denn dein Code macht genau 
das, was er soll. Ich sehe mir das jetzt nochmal genau an.
Dann bitte ich dich nochmal um Geduld, mir das zu erklären.

1. Was passiert bei 45-200 auf Bitebene?

2.

Karl Heinz schrieb:
> GIFR |= ( 1 << INTF0 );

Was macht das? Klappt auch, wenn ich das auskommentiere.

3. Ein Problem habe ich noch: Es gibt noch einen weiteren Kanal, der 
dazu führt, dass die LED an geht (unregelmäßig und immer in anderen 
Hebelstellungen). Hast du eine Idee wieso? Dieser Kanal wird zur Zeit 
nicht per Interrupt erfasst und ist nicht an den uC angeschlossen.

von chris (Gast)


Lesenswert?

Karl Heinz schrieb:
> GIFR |= ( 1 << INTF0 );


da sollte man aber besser das 'oder' weglassen, also:

GIFR = (1<<INTF0);


weil sonst werden alle anderen Interruptflags in GIFR ja auch 
mitgelöscht (z.B. INTF1).

lg
Chris

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:
> Karl Heinz schrieb:
>> GIFR |= ( 1 << INTF0 );
>
>
> da sollte man aber besser das 'oder' weglassen, also:
>
> GIFR = (1<<INTF0);

Danke.
Jetzt wo du es sagst, fällt es mir wie Schuppen von den Haaren. War ein 
Flüchtigkeitsfehler.

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:
> Also erstmal möchte ich dir herzlich danken, denn dein Code macht genau
> das, was er soll. Ich sehe mir das jetzt nochmal genau an.
> Dann bitte ich dich nochmal um Geduld, mir das zu erklären.
>
> 1. Was passiert bei 45-200 auf Bitebene?

es wird ganz normal Bit für Bit subtrahiert.
Nur das das Ergebnis nicht größer als 8 Bit sein kann und das es keine 
Behandlung eines Vorzeichens gibt.

Würdest du es als Zahl mit Vorzeichen ausrechnen, dann erhältst du ein 
negatives Ergebnis. Und dieses Ergebnis hat exakt genau das gleiche 
Bitmuster, wie das gesuchte. Ergo: wird es ohne Vorzeichen 
interpretiert, dann kommt genau das richtige raus.
1
  44        00101100
2
-200        11001000
3
           ----------
4
   11111111 01100100

das wäre das 16 Bit Ergebnis. Als signed Zahl interpretiert, im 2-er 
Komplement, wäre das die Zahl -156. Wir haben aber keine 16 Bit. Wir 
haben nur 8. Und die haben noch  nicht mal ein Vorzeichenbit. Also fällt 
alles vor dem 8. Bit weg. Das Ergebnis ist 01100100. Und das ist das 
Bitmuster für 100 (als unsigned Zahl, was hier zufällig keinen 
Unterschied macht, weil das Bit 7 sowieso 0 ist).

Wäre das ein 16 Bit Timer und würden daher alle 16 Bits gelten, dann 
wäre als signed Zahl interpretiert, das Bitmuster 11111111 01100100 die 
Zahl -156. Aber da wir unsigned rechnen, gibt es keine Sonderstellung 
des höchstwertigen Bits und das Bitmuster beschreibt die Zahl 65380. 
Exakt die Anzahl der Zahlen zwischen 200 und 44, wenn der Overflow bei 
65536 erfolgt.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> 3. Ein Problem habe ich noch: Es gibt noch einen weiteren Kanal, der
> dazu führt, dass die LED an geht (unregelmäßig und immer in anderen
> Hebelstellungen). Hast du eine Idee wieso?

Irgendwas im Sender?
Irgend ein Mischer eingeschaltet?

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:
:
>> GIFR |= ( 1 << INTF0 );
>
> Was macht das? Klappt auch, wenn ich das auskommentiere.

Mit dem INTF0 wird das Auftreten eines Interrupt Ereignisses 
registriert. Sobald das Ereignis eintritt (in diesem Falle eben das 
Auftreten einer Flanke am INT0 Eingang), wird dieses Flag gesetzt.
Denn das heißt noch lange nicht, dass die zugehörige ISR sofort 
losläuft. DIe Interrupts könnten ja auch zu diesem Zeitpunkt gerade 
gesperrt sein. Also muss irgendwo zwischengespeichert werden: Jawohl da 
war was.

Und das macht genau dieses Flag.

In manchen Situationen kann es jetzt passieren, dass diese Flags gesetzt 
werden, wenn man den 'Auslösemechanismus' umkonfiguriert. Das ist zb oft 
dann der Fall, wenn der Auslösemechanismus aus einem voher-nachher 
Vergleich besteht (typischer Fall: Flankenerkennung). Dann will man 
diesen Interrupt-Anforderung in weiterer Folge nicht haben, den die ist 
nur durch die Umkonfiguration entstanden und war nicht real von der 
äusseren Hardware verursacht. In so einem Fall löscht man dann einfach 
das Flag. Und das geschieht, indem man es mit einem 1-Bit beschreibt.

: Bearbeitet durch User
von Sefco (Gast)


Lesenswert?

Danke für die gute Erklärung!

Karl Heinz schrieb:
> Irgendwas im Sender?
> Irgend ein Mischer eingeschaltet?

Habe die Spektrum DX6i und die Monitoring Funktion auf dem Display zeigt 
nicht an, dass an ELEV (mein INT) was passiert, wenn ich THRO bewege. 
Trotzdem geht meine LED an und aus.

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:
> Danke für die gute Erklärung!
>
> Karl Heinz schrieb:
>> Irgendwas im Sender?
>> Irgend ein Mischer eingeschaltet?
>
> Habe die Spektrum DX6i und die Monitoring Funktion auf dem Display zeigt
> nicht an, dass an ELEV (mein INT) was passiert, wenn ich THRO bewege.
> Trotzdem geht meine LED an und aus.

Hmm.
Jetzt wärs gut, wenn man die Zahlenwerte sehen könnte oder das Signal 
mit einem Oszi beobachten könnte.
Apropos Oszi. Wenn du an diesen Kanal ein Servo anschliesst, bewegt sich 
das dann auch? Wenn ja, dann ist der Spuk real und irgendetwas verändert 
tatsächlich die Pulslänge.
EIn Servo als Oszi-Ersatz - das ist neu. Sachen gibts :-)

von Sefco (Gast)


Lesenswert?

Karl Heinz schrieb:
> Hmm.
> Jetzt wärs gut, wenn man die Zahlenwerte sehen könnte oder das Signal
> mit einem Oszi beobachten könnte.
> Apropos Oszi. Wenn du an diesen Kanal ein Servo anschliesst, bewegt sich
> das dann auch? Wenn ja, dann ist der Spuk real und irgendetwas verändert
> tatsächlich die Pulslänge.
> EIn Servo als Oszi-Ersatz - das ist neu. Sachen gibts :-)

Das Servo reagiert nicht. Also kann eigentlich nichts falsches am Puls 
passieren. Wenn ich INT0 von ELEV an RUD hänge, dann hat THRO den selben 
negativen Einfluss.
Egal welchen Impuls ich abgreife von meinen 6 Kanälen, THRO stört. THRO 
ist übrigend mein Fahrtenregler, der den Empfänger mit Strom versorgt. 
Ich denke das hat was damit zutun.

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> Egal welchen Impuls ich abgreife von meinen 6 Kanälen, THRO stört. THRO
> ist übrigend mein Fahrtenregler, der den Empfänger mit Strom versorgt.
> Ich denke das hat was damit zutun.

Hmm.
Könnte es sein, dass da irgendwelche Spikes in der Versorgungsspannung 
oder den Pulsen auftauchen, die zwar das Servo nicht stören, die 
Pulsauswertung hier aber durcheinander bringen?

Versuch mal folgendes.
Du weisst ja, dass eine Flanke aufgetreten ist. Daher wissen wir auch, 
welchen Pegel eigentlich die Eingangsleitung haben müsste (bei einer 
steigenden Flanke muss der Pin auf 1 liegen, bei einer fallenden auf 0). 
Wenn dem nicht so ist, dann ist schon mal was faul.
Weiters wissen wir, dass die ermittelten Längen sich im Zahlenbereich 
nicht beliebig verteilen können, sondern dass die zwischen 32 und 64 
liegen müssen (wenn ich das noch richtig im Kopf habe). Auch das kann 
man testen.

Auf Deutsch: Plausibilitätstests um festzustellen, ob das gemessene real 
sein kann oder nicht (weil "Schmutz" auf den Spannnungen liegt).

von Sefco (Gast)


Lesenswert?

Ich versuche mir aus dem Institut ein Oszi zu leihen und melde mich dann 
wieder ;)

Gute Nacht

von Sefco (Gast)


Lesenswert?

Hey, es gibt Neues:

Beide Interrupt (INT0 und INT1) Routinen sehen so aus (nur mit 
unterschiedlichen LEDs), wie du sie mir gegeben hast.
1
ISR(INT0_vect)
2
{  
3
  static uint8_t startTimeTHRO;
4
  
5
  if ( (MCUCR & (1<<ISC00)) )
6
  {
7
    startTimeTHRO = TCNT0;
8
    // INT0 auf fallende Flanke einstellen
9
    MCUCR &= ~(1 << ISC00);
10
  }
11
  else
12
  {
13
    uint8_t lengthTHRO = TCNT0 - startTimeTHRO;
14
    
15
    
16
    // INT0 auf steigende Flanke einstellen
17
    MCUCR |= (1 << ISC00);
18
19
    if (lengthTHRO > 57)
20
    {    
21
      PORTD |= (1<<ABBLENDLICHT_VORNE);
22
    }
23
    if (lengthTHRO < 37)
24
    {
25
      PORTD &= ~(1<<ABBLENDLICHT_VORNE);
26
    }
27
  }
28
29
  GIFR = ( 1 << INTF0 );  
30
}

Der Timerwert von 57 und 37 ist exakt richtig um meine LED bei ELEV erst 
bei Vollauslenkung des Hebels an- sowie auszuschalten.
Ich sehe die Pulse von THRO und ELEV auf meinem Oszi und sie sind beide 
exakt 1,5 ms lang in Servomittelstellung. Allerdings sind sie 
Phasenverschoben und liegen auf dem Oszi nicht übereinander.

Wenn ich THRO nur minimal auslenke zum Einschalten (Timerwert 57), geht 
sofort die LED an. Teilweise geht sie sogar ganz ohne Hebelaussteuerung 
an. Zum Ausschalten (Timerwert 37) muss ich komplett auslenken.

Das bedeutet, obwohl ich zwei saubere Pulse habe, die genau das selbe 
machen, passiert bei THRO mist. Ich kann mir das einfach nicht 
erklären...

Ich denke damit hängt es auch irgendwie zusammen, dass THRO für ELEV 
einen Interrupt auslöst und meine ELEV Led verrückt spielt.

Zwei weitere Sachen sind mir aufgefallen:
1.) Die Pulse haben bei High nur einen Wert von 3V. Ich dachte das sind 
5V?! Ist das der Grund für meine Probleme?

2.) Wenn ich den ELEV Impuls in der Breite ändere (Hebel bewegen), dann 
schiebt das den THRO Impuls zur Seite auf dem Oszi Display.
Wenn ich den THRO Impuls in der Breite ändere (Hebel bewegen), dann 
schiebt das den ELEV Impuls nicht zur Seite. Er bleibt auf dem Oszi 
Display da wo er ist. Das kann ich überhaupt nicht deuten, ob das 
richtig ist oder nicht.


Ich verstehe die Welt nicht mehr... :(

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> Allerdings sind sie
> Phasenverschoben und liegen auf dem Oszi nicht übereinander.

> 2.) Wenn ich den ELEV Impuls in der Breite ändere (Hebel bewegen), dann
> schiebt das den THRO Impuls zur Seite auf dem Oszi Display.

zunächst mal: ja, das ist alles richtig und war auch so zu erwarten.
Das Verhalten erklärt sich dadurch, dass die Pulse ja nacheinander über 
die Funkstrecke gehen. Der Empfänger sortiert sie nur noch nach der 
richtigen Ausgangsnummer auseinander.
Zumindest war das bei den alten FM-Anlagen so. Ich denke Spektrum hat 
das nachgebildet. Denn das hat auch einen VOrteil: die Servos kriegen 
die Pulse zeitlich versetzt. Da (Analog-) Servos die interne Regelung 
nur beim erhalt eines Pulses anwerfen, bedeutet das auch, dass die 
Servos NACHEINANDER die Position nachregeln. Was wiederrum bedeutet, 
dass es nicht passiert, das alle Servos gleichzeitig Strom vom Akku 
ziehen.

> Wenn ich den THRO Impuls in der Breite ändere (Hebel bewegen), dann
> schiebt das den ELEV Impuls nicht zur Seite. Er bleibt auf dem Oszi
> Display da wo er ist. Das kann ich überhaupt nicht deuten, ob das
> richtig ist oder nicht.

Passt. Das bedeudet einfach nur, dass im Impulsdiagramm auf der 
Funkstrecke ELEV vor THRO übertragen wird.

Aber: das löst das Problem nicht und hat auch nichts damit zu tun. Denn 
dem Code ist die Phasenverschiebung der Pulse ja egal, solange die 
zeitliche Länge der Pulse gleich bleibt.

Hmm. Die 3V könnten natürlich ein Problem sein.

Ansonsten hab ich momentan auch keine Idee mehr. Poste mal den 
kompletten Code und nicht nur den einen Ausschnitt. Vielleicht fällt mir 
was dazu ein.

: Bearbeitet durch User
von Sefco (Gast)


Angehängte Dateien:

Lesenswert?

Anbei mein Code.

Das Problem, dass die LED bei THRO zu früh (also bei nur geringer 
Hebelauslenkung) angeht, ist behoben, wenn das Oszi aus ist und meine 
Messkabel ( GND, INT1 an CH1 und INT0 an CH2) aber noch drin stecken. 
Kannst du dir darauf einen Reim machen?


Abgesehen davon, geht halt die ABBLENDLICHT_HINTEN an, wenn 
ABBLENDLICHT_VORNE angeht. ABBLENDLICHT_HINTEN geht aber nicht mehr aus, 
wenn ich ABBLENDLICHT_VORNE ausschalte.

von Sefco (Gast)


Lesenswert?

Ich baue mir morgen in der Uni erstmal einen Pegelwandler von 3V auf 5V 
und klopp den rein.

von Karl H. (kbuchegg)


Lesenswert?

Sefco schrieb:

> Kannst du dir darauf einen Reim machen?

Nicht wirklich.

(Wechsel in den Probier-Modus)
Hast du schon mal versucht die Pullups an den Interrupt Pins 
einzuschalten? Obwohl ... sollte eigentlich keinen Unterschied machen. 
So ein Empfänger sollte schon ordentliche Pegel liefern können.

Ich seh nichts mehr. Meiner Meinung nach müsste das klappen.

von Sefco (Gast)


Lesenswert?

Ich habe jetzt mal den Seitenschneider angreifen lassen und habe mal den 
Puls vom THRO abgenommen und mal an RUD gehangen. Beide LEDs schalten 
wie sie sollen...dachte ich. Wenn ich mal ganz schnell hintereinander am 
Hebel mit dem Puls für INT0 wackel, dann habe ich wieder das selbe 
Problem, dass die LED in INT1 geschaltet wird, obwohl sich der Puls an 
INT1 nicht bewegt.

Vielleicht ist einfach der Kontroller am Arsch. Ich habe einen neuen 
ATMEGA16A bestellt.

von Sefco (Gast)


Lesenswert?

Ich habe was rausgefunden Karl Heinz:

Es gibt Störpulse, aber wirklich nur ganz kurze Peaks die man auch nur 
ab und an auf dem Oszi sehen kann wenn man hinschaut.
Also habe ich folgendes gemacht:
1
// Interrupt Routine für ELEV 
2
ISR(INT1_vect)
3
{
4
  
5
  static uint8_t startTimeELEV;
6
  
7
  if ( (MCUCR & (1<<ISC10)) )
8
  {
9
    startTimeELEV = TCNT0;
10
    // INT1 auf fallende Flanke einstellen
11
    MCUCR &= ~(1 << ISC10);
12
  }
13
  else
14
  {
15
    uint8_t lengthELEV = TCNT0 - startTimeELEV;
16
    
17
    
18
    // INT0 auf steigende Flanke einstellen
19
    MCUCR |= (1 << ISC10);
20
21
    if (lengthELEV > 57)
22
    {
23
      if( lastPulsLengthELEV == lengthELEV ) PORTD |= (1<<ABBLENDLICHT_HINTEN);
24
    }
25
    if (lengthELEV < 37)
26
    {
27
      if( lastPulsLengthELEV == lengthELEV ) PORTD &= ~(1<<ABBLENDLICHT_HINTEN);
28
    }
29
    lastPulsLengthELEV = lengthELEV;
30
  }
31
32
  GIFR = ( 1 << INTF1 );
33
  
34
}

Der Puls muss mit der detektierten Länge zweimal auftreten, damit 
geschaltet wird. Und zack sind meine Störungen weg.

von Sefco (Gast)


Lesenswert?

Es sind Kanalübergreifende Störpeaks, die beim uC zur Interrupt 
Auslösung führen und dann zum Chaos weil sie sau kurz sind. Der uC ist 
so schnell das er das merkt, während meine Servos das kalt lässt.

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.