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ß
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.
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?
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.
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.
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
> 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?
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.
OK, ich arbeite das Tutorial nochmal durch und probiere es mit einer LED. Ich melde mich dann wieder.
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.
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
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
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.
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ß
> 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
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.
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.
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.
> 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.
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?
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
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
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?
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
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
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
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.
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
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.
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
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?
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
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.
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 :-)
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.
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).
Ich versuche mir aus dem Institut ein Oszi zu leihen und melde mich dann wieder ;) Gute Nacht
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... :(
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
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.
Ich baue mir morgen in der Uni erstmal einen Pegelwandler von 3V auf 5V und klopp den rein.
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.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.