Hallo Zusammen, seit einigen Tagen versuche ich einen Kanal eines RC Empfängers auszulesen denn ich an INT0 eines atmega32 angeschlossen habe. Ablauf: -Nach Auftritt der steigenden Flanke an INT0 starte ich den Timer 0 (ISR gesteuert) im CTC Modus und inkrementiere eine Variable beim Erreichen des OCR0 Wertes (ISR gesteuert). -Beim Erkennen der fallenden Flanke (ISR gesteuert) stoppe ich den Timer 0 bis die nächste steigende Flanke erkannt wird und gebe die Variable aus. -Der Timer arbeitet mit 16 MHz, also Vorteiler 1 und der OCR0 Wert ist auf 15 Eingestellt also 1Mhz. Somit wird theoretisch jede Mikrosekunde die Variable hoch gezählt. Die Zeit zwischen einer steigenden- und fallenden Flanke beträgt zwischen 1000 und 2000 Mikrosekunden, aber als Ergebnis bekomme ich immer etwas zwischen 192 und 330. Kann der AVR überhaupt so schnell zählen? Theoretisch hat er 16 Takte um die Variable zu inkrementieren. Ist das ausreichend? Beschäftige mich erst seit kurzem mit AVR’s, also bitte nicht erschlagen wenn ich etwas Grundlegendes nicht verstehe! Den Quelltext liefere ich heute Abend nach. Schon Mal Danke und Gruß Rainer
Nimm die InputCapture-Einheit. Die macht das alles alleine. Und: Nein, du bist der allererste, der ein RC-Empfangssignal auswerten will.
@ Stefan: Den code stelle ich heute Abend ein, bin derzeit nicht an meinem Rechner. @Paule: Es geht mir ums Verständnis, eine Funktionierende Lösung habe ich auch mit dem Timer1.
> Kann der AVR überhaupt so schnell zählen?
Ja kann er, vor allem die Timer, denn dafür sind sie gemacht.
Irgendwo hast Du sicher einen fehler im Programm.
Innerhalb von 16 Taktzyklen wird deine Interrupt-Routine sicher nicht fertig. Das heißt, es gehen viele Timer-Interrupts verloren. Ich würde es so machen: * Statt Timer0 den Timer1 verwenden, denn der zählt bis maximal 65535 (16 Bits). * Den Prescaler auf 8 setzen (Register TCCR1B). 16 wär einfacher, aber diese Option gibt es hier nicht. * An Stelle einer eigenen Variablen einfach das Zählregister des Timers auf 0 setzen und am Ende auslesen (TCNT1L und TCNT1H, auf die Reihenfolge achten). * Das Ergebnis halbieren (Assembler-Befehl "lsr").
Rainer schrieb: > Kann der AVR überhaupt so schnell zählen? Theoretisch hat er 16 Takte um > die Variable zu inkrementieren. Ist das ausreichend? Für das bloße Inkrementieren einer Variable wäre das ausreichend. Aber für den kompletten Interrupt nie und nimmer. Allein Ein- und Aussprung brauchen schon 11 Takte, und dazu kommt dann auch noch das Sichern und Wiederherstellen der verwendeten Register.
Danke Euch allen, damit habe ich auch die Antwort auf meine Frage – es geht nicht :( Dann muss ich mir etwas anderes überlegen. Da der Timer1 später anderweitig verwendet wird, wird es wohl nicht so einfach das Ganze zu realisieren. Trotzdem Vielen Dank für die Hilfe. Gruß Rainer
Rainer schrieb: > Danke Euch allen, > > damit habe ich auch die Antwort auf meine Frage – es geht nicht :( > Dann muss ich mir etwas anderes überlegen. Da der Timer1 später > anderweitig verwendet wird, wird es wohl nicht so einfach das Ganze zu > realisieren. Langsam. Niemand sagt, dass man einen Timer nur für eine Sache verwenden kann. Der kann auch, bei geschicktem Vorgehen, dazu benutzt werden, mehrere Sache zu behandeln. Und oft genug beginnt dieses 'geschickte Vorgehen' damit, dass man sich darüber im Klaren ist, dass dieses 'Wenn ... dann setze ich den Timer auf 0' Käse ist. Dieses Timer bewusst starten/stoppen/0-setzen braucht in Wirklichkeit meistens kein Mensch. Lass den Timer durchlaufen und nimm die numerische Differenz zwischen den Zählerständen 2-er Ereignisse und du hast genau das gleiche Ergebnis. Wenn du unsigned rechnest und der Timer in der bewussten Zeit gar nicht bis 65535 zählen kann, dann brauchst du einen Overflow arithmetisch noch nicht mal speziell berücksichtigen. Du rechnest einfach immer Ende-Anfang und es kommt durch die unsigned-Regeln IMMER das richtige Ergebnis raus. Lässt du beispielsweise bei 16Mhz den Timer 1 durchlaufen (bei Prescaler 1), dann bewegt sich die Differnz zwischen der steigenden und der fallenden Flanke eines Servosignals in der Größenordnung von 16000 bis 32000. Ob du dabei Taktgenau den Timerstand abgreifst, spielt praktisch gesehen kaum eine Rolle, denn kein Servo kann tatsächlich seinen Servoweg in 16000 Positionen reproduzierbar auflösen. D.h. man wird die festgestellte Differenz der Zählerstände sowieso sofort verringern, wodurch dann auch der eine oder andere verpasste Timertakt sich rausmittelt.
Rainer schrieb: > damit habe ich auch die Antwort auf meine Frage – es geht nicht :( > Dann muss ich mir etwas anderes überlegen. Da der Timer1 später > anderweitig verwendet wird, wird es wohl nicht so einfach das Ganze zu > realisieren. Nicht so leicht aufgeben. :-) Ich wusste nicht, dass der Timer1 nicht zur Verfügung steht. Du kannst trotzdem die Lösung verwenden, die ich oben skizziert habe. Der Zähler des Timer0 läuft dann nur bis 255, das ist für dich das Low-Byte. Über den Timer-Overflow-Interrupt kannst du quasi manuell das High-Byte weiterzählen. Natürlich passiert es dadurch, dass du den Zähler nicht mikrosekundengenau stoppen kannst, wenn er grad dabei ist, das Hihg-Byte zu erhöhen. Mit dieser kleinen Ungenauigkeit müsstest du leben. Alternativ könntest du versuchen, die Interruptroutine in Assembler mit ein paar Tricks innerhalb der 16 Takte zum Laufen zu kriegen. Die restliche Rechenleistung des µC sinkt dann aber gewaltig ab. Oder du misst die Zeit aktiv über Zählschleifen. Das geht natürlich nur, wenn der Mikrocontroller während dieser Zeit keine anderen Aufgaben erledigen muss. Andere Frage: Steht denn der Timer2 auch zur Verfügung? Du könntest ihn parallel zum Timer0 laufen lassen. Der Timer2 besitzt einen eigenen Prescaler, du könntest ihn zum Beispiel mit 1/128 der Geschwindigkeit des Timer0 laufen lassen. Aus beiden gezählten Werten kannst du dann ohne Weiteres und ohne Umschaltfehler die tatsächliche Zeit ausrechnen.
Nochmals Danke, werde versuchen das Ganze als erstes mit dem Timer1 zu realisieren, wie Karl Heinz vorgeschlagen hat. Dann muss ich nur den Zwischenwert nach einem Overflow speichern und diesen anschließend verrechnen. Und in der ISR bei einer fallenden Flanke eine Fallunterscheidung durchführen ob ein Overflow stattgefunden hat. In etwa so: ISR(INT0_...){ if(steigende Flanke){ berechne= 1; start = TCNT1; return; } if(fallende Flanke){ stop = TCNT1; if(ueberlauf){ signal = zwischenwert + stop zwischenwert = 0; ueberlauf = 0; }else{ signal = stop – start; } } berechne = 0; start = 0; stop = 0; } ISR(OVF_..){ if(berechne){ ueberlaf++; zwischenwert = (timer_max - start) } } Bin gespannt ob das heute Abend so funktioniert :) Gruß Rainer
Rainer schrieb: > Nochmals Danke, > > werde versuchen das Ganze als erstes mit dem Timer1 zu realisieren, wie > Karl Heinz vorgeschlagen hat. Dann muss ich nur den Zwischenwert nach > einem Overflow speichern Nö. Nicht nach einem Overflow. Du lässt den Timer laufen und wenn dein externer Interrupt (für die Signalflanke) kommt, dann liest du den Zahlenwert des Timers aus (oder du benutzt den Input Capture, was dasselbe macht nur etwas genauer und du hast nur 1 Input Capture Eingang) Du brauchst dazu keinen Overflow. Bildlich gesprochen hast du eine Armbanduhr, deren Sekundenzeiger ständig kreist (das ist der Timer). Immer wenn du die Flanke siehst, schaust du auf die Uhr und siehst nach, wo der Zeiger gerade steht. 2 dieser Informationen kannst du dann verrechnen, wieviele Sekunden dazwischen lagen. Aber wie oft der Sekundenzeiger dabei die 0 überquert hat (und 1 Minute weitergeschaltet hat) interessiert dich überhaupt nicht, solange du weißt, dass maximal 1 Minute zwischen den Ereignissen lag.
Hallo Karl Heinz, eventuell verstehe ich etwas falsch, aber was passiert wenn die steigende Flanke zu einem Zeitpunkt kommt, kurz bevor der Timer1 den Endwert 65535 erreicht. Da der Timer1 noch eine weitere Aufgabe hat, möchte ich ihn bei einer steigenden Flanke nicht auf 0 setzen. Wenn ich von 16000 bis 32000 Takten für ein Signal ausgehe. Beispiel 1: Der Timer1 arbeitet im Normalmodus und die steigende Flanke kommt bei TCNT1 = 60535. Somit habe ich als Startposition 60535. Nach 65535 fängt der Timer1 bei 0 an. Somit befindet sich mein Endwert (fallende Flanke) irgendwo zwischen 11000 und 27000. Beispiel 2: Ein anderes Mal könnte die steigende Flanke bei z.B. TCNT1 = 10 auftreten und mein Endwert würde sich irgendwo zwischen 16010 und 32010 befinden. Die Verrechnung im Beispiel 1 und Beispiel 2 unterscheidet sich doch. Oder stehe ich gerade auf dem Schlauch? Gruß Rainer
Rainer schrieb: > Die Verrechnung im Beispiel 1 und Beispiel 2 unterscheidet sich doch. Nein, tut sie eben nicht, wenn du innerhalb von unsigned-16-Bit rechnest. 11000 - 60535 = 16001
Rainer schrieb: > Hallo Karl Heinz, > > eventuell verstehe ich etwas falsch, aber was passiert wenn die > steigende Flanke zu einem Zeitpunkt kommt, kurz bevor der Timer1 den > Endwert 65535 erreicht. Da der Timer1 noch eine weitere Aufgabe hat, > möchte ich ihn bei einer steigenden Flanke nicht auf 0 setzen. Musst du ja auch nicht. Unsigned Rechnerei berücksichtiggt das für dich. Zwischen 65554 und 1 liegen 3 Takte 65534 65535 0 1 | | | | +------+-----+---+ 1 2 3 und unsigned gerechnet ergibt 1 - 65534 genau diese 3. Binär 000000000000001 - 111111111111110 ------------------ 1000000000000011 Das ist aber ein 17-Bit Ergebnis, d.h. die vorderste 1 fällt weg (UNterlauf) und das 16-Bit Ergebnis lautet 000000000000011 also 3 Du rechnest einfach unsigned int start; unsigned int end; unsigned int differenz = end - start; und durch unsigned kommt IMMER das richtige raus: die Anzahl Werte dazwischen. Selbst dann, wenn da ein Overflow im Spiel ist. Es ist gerade dieses nicht berücksichtigen dieses Overflows, welches das möglich macht.
Danke Euch, wieder etwas gelernt, es läuft super. :) Gruß Rainer
Noch ein hinweis: Falls du mehr als einen kanal brauchst, versuch den Empfänger auf "Summensignal" umzustellen, der Code wird unwesentlich länger, und du hast auf einen Schlag alle 6 oder 12 oder 18 Kanäle...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.