Hi *, ich bin ziemlich verwirrt. Ich habe ein Programm für den ATmega644 geschrieben, das durchaus einige Interrupts und ISR besitzt. Im Betrieb der fertigen Hardware allerdings fällt auf, dass sporadisch Interrupts nicht auslösen. Ich habe dann im erzeugten lss nachgesehen und mußte feststellen, dass an diversen Stellen innerhalb von main() innerhalb der Hauptschleife (z.B. immer vor Aufrufen / Sprungbefehlen an sprint_f, ca 20 Zeilen vorher) "cli"s eingefügt werden, die ich bestimmt nicht im C-Code habe. Ich habe dagegen nur ein einziges (selbst gesetztes) sei() im C-Code, und auch in der lss-Datei ist dieses sei an der richtigen, dem C-Code entsprechenden Stelle gesetzt. In der lss gibt es auch nur dieses einzige sei. Passiert es nun, dass die Kombination aus compiler-seitigem cli und eigenem sei sehr weit auseinander liegen, so werden eine Reihe von ISR-Sprüngen/Interrupts nicht erfasst. Wie kann ich dem Kompiler denn abgewöhnen, cli's zu setzen? Danke, Christoph
Kannst du versuchen ein abgespecktes Beispiel zu machen, welches den Fehler zeigt? Wenn das reproduzierbar auftritt, wäre das in der Tat ein nicht zu vernachlässigendes WinAVR Problem. Dazu bitte die Compilerversion angeben.
Hi, Danke Dir, ich glaube ich hab übersehen, dass der Compiler natürlich das SREG sichert vorher und auch nach den Operationen wieder zurückschreibt, bei allen jeweiligen Stellen. Auch sind die Operationen nicht länger als 2 Taktzyklen, in denen das globale IEF nicht gesetzt ist. Ich kann mir die Fehlfunktion dann nicht erklären. Denn auch wenn mit cli das Flag gelöscht wurde, wird ja beim Setzen der jeweilige Interrupt wieder ausgeführt, nicht wahr? Und angenommen, das Betriebsszenario würde immer 4 externe Interrupts im Millisekundenbereich hintereinander auslösen, so müßten doch die restlichen 3 dann einwandfrei erkannt werden? Es wird aber keiner erkannt. Compiler-Version ist 4.3.2, WinAVR 20090313; Doku der Software (und diese mit allen Files) gibt's hier: http://www.mcseven.me/2010/08/pimp-my-kicker-a-k-a-wham-o-meter/4/ Download ganz oben. Ich weiß leider nicht, wie ich das sonst komprimieren oder abspecken könnte, ohne wichtiges zu verlieren. Danke.
Christoph Söllner schrieb: > Wie kann ich dem Kompiler denn abgewöhnen, cli's zu setzen? Garnicht. Die braucht er immer, wenn er einen Stackframe anlegt, da der Stackpointer keinen atomaren Zugriff kennt. Ist aber kein Problem, da ein Stackframe anlegen wesentlich kürzer dauert, als jeder Interrupthandler. Interrupts dürfen nicht so oft kommen, daß keine Zeit mehr für das Main übrig bleibt. Peter
1. Du liest PINA in dem Interrupt PCINT0_vect immer wieder neu ein. Damit erhöhst Du die Wahrscheinlichkeit, dass Du Zustände einliest die dem ursprünglichen garnicht mehr entsprechen. Speichere den Zustand am Anfang in einer Variablen und speichere in dann am Ende in oldPortA um. 2. Ich würde an Deiner Stelle nicht in der Interruptroutine die jeweils ausgelösten Lichtschranken unterscheiden, sondern nachher in der Auswertung. Dann dauert die Interruptroutine nicht so lange. D.h. also bei jeder Änderung der Lichtschrankenzustände den kompletten Zustand speichern und den aktuellen Timerwert diesem zuordnen. Nachher, wenn alle Lichtschranken freigegeben worden sind, kannst Du die längste Folge von Frei-Unterbrochen-Frei heraussuchen. Prellen sollte dann vielleicht extra behandelt werden, bzw. habe ich gerade Zweifel ob so eine Lichtschranke prellt, wenn Du nicht gerade ein Relais als Ausgangsschaltung hast. 3. Dieses Suchen nach der längsten Unterbrechung solltest Du nicht in einem Interrupt machen, sondern in main. 4. Du solltest untersuchen, ob Du in main überhaupt genug Zeit hast alles zu bearbeiten. Dazu einen bzw. zwei freie Portpins benutzen die Du setzt bzw. löschst, wenn Du in den Interrupt bzw. main eintrittst und es wieder verlässt.
Ah, das sind natürlich alles valide Punkte; 4 glaube ich bestätigen zu können, nachdem in main() sowieso alles unterbrechbar ist. Wenn ich das so wie in 2) code, hab ich nur noch Schwierigkeiten der Synchronisation, d.h., nachdem ein PCINT auslöst, weiß ich ja noch nicht, ob unterbrechend oder wiederherstellend; eventuell ginge es so:
1 | struct tRecord { |
2 | uint16_t timerValue; |
3 | uint8_t pinA; |
4 | uint8_t pinC; |
5 | uint8_t pinD; |
6 | } timerValues[50]; //24*2 worst-Case + 2 Sicherheit, spart malloc, 250 Bytes |
7 | uint8_t tRecordIndex; |
8 | volatile uint8_t syncVar=0; |
9 | |
10 | ISR(PCINT0_vect) { |
11 | // Keine Messung, wenn main() noch nicht fertig.
|
12 | if (syncVar) { return; } |
13 | timerValues[tRecordIndex].timerValue = TCNT1; |
14 | timerValues[tRecordIndex].pinA = PINA; |
15 | timerValues[tRecordIndex].pinC = PINC; |
16 | timerValues[tRecordIndex].PinD = PIND; |
17 | tRecordIndex++; |
18 | if (!(PINA | PINC | PIND)) { syncVar = 1; } |
19 | }
|
20 | ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect)); |
21 | ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect)); |
? Ich werd mich nachher mal ans coden machen und dann berichten...
Hi nochmal, ich habs jetzt so gecoded, dass die ISR im wesentlichen wie oben ausschaut, und das Hauptprogramm rechnet. Die Messwerte schauen jetzt plausibel aus, Tore wurden bisher alle gezählt, nur leider sind die Intervalle noch immer zu groß. Ich habe die Vermutung, dass das etwas physikalisches ist, und der Ball dank eines schrägen Holzbrettes im Tor (zum Abbremsen montiert) irgendwie hängen bleibt und zu lange in der Lichtschranke verbleibt. Ich werde mit einem Logic-Analyser einmal die Zeiten messen und zum anderen einmal die Barriere im Kicker entfernen, mal schauen, ob's dann besser wird.
Hi. Habe leider erst jetzt Dein Post von heute morgen gelesen. Zum ersten freue ich mich, das was sinnvolles bei meinen Bemerkungen für Dich dabei ist. Ich bitte Dich den Code mal im aktuellen Stand hier als Anhänge zu posten. Es ist mir zu mühsam über Deine Seite zu scrollen mit der Unsicherheit ob das nun aktueller Code ist oder gar ein ZIP zu laden, zu entpacken etcpp. Mache es uns bitte so leicht wie möglich Dir zu helfen. OK. Soweit so gut. Das sieht ja alles ganz vielversprechend aus. Du gehst hier auf drei Details ein, die aber im Grunde nur verschiedenene Aspekte des selben Dings sind: 1. Reicht die Zeit in main? (Punkt 4) 2. Synchronisation (das hast Du dazugedacht) 3. Intervalle zu gross (was wie ich vermute heisst, das manche Tore nicht erkannt werden) Ob main unterbrechbar ist, wie Du schreibst, oder nicht hat mit der Frage danach ob Du in main genug Zeit hast nichts zu tun. Gehen wir von einem eingehenden Ball aus, so gibt es eine gewisse maximale Zeit für die Bearbeitung der Interrupts, der Auswertung (in main) und der Anzeige (in main). Idealerweise ist die Summe aus beidem kleiner als die kürzestmögliche Zeit zwischen Zwei Toren, denn je Tor wird gesamte Zeit gebraucht. In dem Zusammenhang vermute ich, das Du mit Punkt 3. auf eine solche hohe Torfrequenz kommst, das die Zeit eben nicht mehr reicht. Aber das kann eigentlich nur beim Testen so sein, denn es dauert ja eigentlich im Sekundenbereich, ehe Du den Ball wieder aus der Ausgabe geholt und in das Spielfeld eingeworfen hast. Ist mein Vermutung wegen 3. also falsch? Was Du effektiv mit 2. Synchronisation meinst, ist mir nicht klar. An sich sehe ich keine Notwendigkeit für eine Synchronisation. Dein Code lässt den Interrupt nur zu wenn sync null ist, aber nach welchem Kriterium das eins wird, schreibst Du nicht (Bitte Code posten). Ich sehe eigentlich keine Notwendigkeit den Interrupt mal zuzulassen oder nicht. (Abgesehen davon wäre ein disablen des INT0 etc sowieso günstiger als das if in der ISR). Die Information wann ein "Tor beginnt" steckt ja schon in dein Einträgen des Arrays timerValues. An sich könntest Du einfach stur immer wieder den ISR bei Änderungen des Portzustandes aufrufen und das Array als Ringpuffer betreiben. Das gerade aktuelle Problem sehe ich darin, zu klären, was Du genau mit Punkt 3 meinst und wozu aus Deiner Sicht die Synchronisation dienen soll. Sieht gut aus, denke ich.
Hm. Mir fällt gerade wieder ein, was ich heute Nacht noch dachte: An sich kannst Du auch schon, noch während der Ball läuft zumindest einen Teil der Daten abarbeiten. Das gibt auch nochmal Zeitreserven.
Ah klar, das hätt ich auch gleich machen können. Anbei die modifizierte kickerspeed.c, ansonsten wurde nichts geändert gegenüber dem ZIP der Webseite. Der Code ist nicht auf Speicherplatz optimiert... Wegen den Intervallen oben meinte ich, dass die Differenz zwischen Start- und Endwert sporadisch noch immer zu groß ist. Aber: Die Werte erscheinen zumindest plausibel. Das heißt, dass die erste Unterbrechung immer bei TCNT1=0 stattfindet (Prescaler 64!) und daher meine ISR vor dem Kopieren in Zeile 7 weniger als 64 ASM Befehle abarbeitet:
1 | ISR(PCINT0_vect) { |
2 | // Keine Messung, wenn main() noch nicht fertig.
|
3 | if (syncVar) { return; } |
4 | |
5 | if (!(TCCR1B&0b00000011)) { |
6 | TCCR1B = 0b00000011; // Timer an |
7 | TCNT1 = 0; |
8 | }
|
9 | |
10 | timerValues[tRecordIndex].timerValue = TCNT1; |
11 | timerValues[tRecordIndex].pinA = PINA; |
12 | timerValues[tRecordIndex].pinC = PINC; |
13 | timerValues[tRecordIndex].pinD = PIND; |
14 | if (tRecordIndex<49) { tRecordIndex++; } |
15 | // sicherstellen, dass die Lichtschranken alle wieder geschlossen sind...
|
16 | if (!(PINA | PINC | PIND)) { |
17 | _delay_us(50); // SW-seitiges Entprellen |
18 | if (!(PINA | PINC | PIND)) { |
19 | syncVar = 1; |
20 | TCCR1B = 0b00000000; // Timer aus |
21 | }
|
22 | }
|
23 | }
|
Auch die anderen Start- und Endwerte sind plausibel, hier einmal ein fiktives Beispiel (Ball hat 2, 3 und 4 unterbrochen): timerValuesStart[2] = 112, timerValuesEnd[2] = 1100 timerValuesStart[3] = 0, timerValuesEnd[3] = 1200 timerValuesStart[4] = 168, timerValuesEnd[4] = 1090 Zur Torfrequenz: Die liegt vielleicht bei 0,1Hz - 0,02Hz ;) Also ja, da hat main() dicke Zeit zum Rechnen. Außerdem sperre ich die Messung, bis main() fertig ist (siehe Code, suche nach "syncVar"). Jetzt ist es nur bei einem extrem schnellen Ball so, dass die Werte sporadisch (10%) etwa so ausschauen: timerValuesStart[2] = 112, timerValuesEnd[2] = 5100 timerValuesStart[3] = 0, timerValuesEnd[3] = 6200 timerValuesStart[4] = 168, timerValuesEnd[4] = 5090 Ich kann mir das nicht mehr durch einen SW-Bug erklären, sondern nur noch durch etwas Physikalisches, kurz: Dass der Ball noch in der Lichtschranke abgebremst wird. Siehe PNG 1. Ball fliegt volley ins Tor und wird von der schwarzen Holzplatte noch in der LS gebremst, bevor er zum Auslauf rollt. In PNG2 ist es richtig: Fliegt er tief ein, so wird er erst hinter der Lichtschranke gebremst und die Messung erscheint mit ~30km/h relativ plausibel.
Hmm. Ich sehe hier zwei mögliche Entwicklungsstränge. Erstmal habe ich ja einige Vorschläge gemacht und kritische Anmerkungen, die nicht berücksichtigen ob der Ball zurückprallt oder nicht. Wenn man auf diesem Weg bleibt, so müsste man die Konstruktion des Tores ändern oder die Lichtschrankenposition. Weiss nicht ob das möglich ist oder gewünscht. Es bleiben die Fragen: 1. Warum benutzt die die sync-Variable? Ich sehe keine Notwendigkeit dafür, wenn main genug Zeit hat. 2. Den Vorschlag Nr. 1 in meinem Posting 26.08.2010 16:00 hast Du nicht in Betracht gezogen, oder? 3. Was mir noch zusätzlich einfällt, ist das Du wahrscheinlich mehrere Interrupts beim gleichen Ball bekommst, wenn der Ball zwei Lichtschranken, die an unterschiedlichen Ports angeschlossen sind, unterbricht. Da bekommst Du dann "doppelte" Einträge. 4. Der Timer kann einfach durchlaufen. Den brauchst Du nicht immer neu zu starten. Der mögliche Überlauf muss dann natürlich behandelt werden. 5. Zusätzlich wäre es möglich die einlaufenden Port-Zeit-Informationen in einem Ringbuffer zu organisieren um den Ablauf der Auswertung klarer und einfacher zu machen. Der zweite Strang ergibt sich jetzt aus der schrägen Torrückwand. An sich ist die Folge und der zeitliche Abstand in welchem die Lichtschranken durch den Rand_ unterbrochen wird _auch spezifisch für die Geschwindigkeit. Wenn Du diese auswertest würde ein abschliessende Abprallen einfacher zu ignorieren sein. Es ist natürlich psychologisch schwer sich jetzt noch umzustellen. Man denkt man hat die Lösung und es hakt eigentlich nur noch an Kleinigkeiten. ;-)
Was ich noch sagen wollte: Diese "etwas so" oder "fiktiven" Beispiele sind vielleicht besser durch reale Werte zu ersetzen um die Situation zu beurteilen. Gib die am besten mal auf ein Terminal aus. Du könntest auch die Mimik mal testen, in dem Du die Lichtschranken definiert unterbrichst. Etwa mit einer schrägen Laufbahn auf der Du die Kugel aus dem Stand abrollen läßt. Damit wird das Einfallen (und Abprallen) etwa mit immer den gleichen Geschwindigkeiten geschehen und die Zeiten ähnlicher, also reproduzierbarer (eigentlich hat das ja keine Steigerungsform, aber Du weisst vielleicht was ich meine).
Hi, Danke Dir. Also, zu Deinen Fragen: 1. Warum benutzt die die sync-Variable? --------------------------------------- Naja, ich muss doch wissen, welche Werte zu einer aktuellen Messung gehören. Dafür muss ich dem rechnenden Teil signalisieren, wenn eine Messung vollständig ist. Das mache ich in der ISR: - Erste beliebige Unterbrechung auf A, C oder D startet den Timer - Nachfolgende Unterbrechungen werden (!PINA&&!PINC&&!PIND) ganz sicher nicht erfüllen, da mindestens eine unterbrochen ist. - Letzte Wiederherstellung einer Messung wird die Bedingung dann erfüllen, SW-Entprellen über 50 uS, und über Sync-Var dem main() mitteilen, dass es jetzt ein Tor gegeben hat. Mit einem Ringbuffer kann ich das nur schwer realisieren, weil noch Messungen des alten Tores vorhanden sind. Ich müßte Start und Ende des betreffenden Ringbuffer-Bereichs speichern, mehr Aufwand. Und im Hauptprogramm soll die Berechnung pro Tor ja nur einmal statt- finden, das würde ich ungern über noch mehr Zustände abbilden. 2. Den Vorschlag Nr. 1 in meinem Posting 26.08.2010 16:00 [...]? ----------------------------------------------------------------- Für die erste Version der Software hatte ich das, es hat aber die ISR nicht verkürzt, daher habe ich die Abfragen der Pin-Toggles in das main() verlegt, wie Du gesagt hast. Für die jetztige Version glaube ich braucht man das nicht, denn a) wird der gültige Zustand von PINA,C,D gleich gespeichert, und das ist ja das wichtigste, und b) unten beim Abfragen mache ich ja ein Software-seitiges Entprellen. Damit stelle ich fest, ob eine Wieder- herstellung aller LS dauerhaft ist. Es würde wenig bringen, die ori- ginalen Zustände von nur ein paar Taktzyklen früher zu erhalten. 3. Was mir noch zusätzlich einfällt, [...] ------------------------------------------ Völlig richtig. Der Ball unterbricht immer mindestens vier LS, sprich ich erhalte mindestens 8 Interrupts, die alle in die gleiche ISR laufen. Die ISR hat bis zum unteren IF-Statement knapp 100 ASM-Befehle, die bei 10MHz in 10uS abgearbeitet sind. Würden tatsächlich zwei exakt gleichzeitig unterbrochen, so würde einer mit ~10 uS Verzögerung aus- gelöst, was bei einem Prescaler von 64 einem Timerwert von 2 oder 3 entspricht, und möglicherweise doppelte Einträge erzeugen, das macht aber nichts, da ich ja nur den längsten benötige. 4. Der Timer kann einfach durchlaufen. -------------------------------------- Hm, ne, ich glaube nicht. Einerseits weil ich dann ständig am Über- laufen habe; beim Timerüberlauf aber soll die Messung gestoppt werden, weil dann der Ball extrem langsam war (~0,3km/h). Andererseits benutze ich ihn zum synchronisieren. Ist er aus, weiß ich, dass eine neue Messung gestartet werden soll. 5. Ringbuffer [...] ------------------- Das verstehe ich nicht. Selbst wenn ich die Meßwerte in einen RB schreibe, wie soll main() herausfinden, welche der 50 Werte-Paare aus TimerZeit und Port-Zuständen jetzt für die aktuelle Messung gegolten haben? 6. Schräge Bahn --------------- Das hab ich schon probiert, leider macht auch da die Physik einen Strich durch die Rechnung; der Ball springt beim Übergang schräg/ horizontal an der Tischplatte auf und verliert jedesmal dadurch unterschiedlich viel Energie. 7. Reale Meßwerte ----------------- Hier einmal reale Meßwerte von vier Torschüssen und ihre KM/H: NR TVSTART TVEND DIFFERENZ KMH 06 00420 01428 01008 07 00055 01680 01625 08 00000 01683 01683 11,363 09 00171 01441 01270 ############# 22 00249 1322 01073 23 00054 1522 01468 24 00000 1553 01553 12,314 ############# 11 02459 07990 05531 12 00324 09410 09086 13 00000 09610 09610 1,990 14 00617 08695 08078 ############# 13 07000 04845 03145 14 00712 04711 03999 4,782 15 00299 04065 03766 16 00000 03321 03321 17 00672 01609 00937 8. Unterbrechung Rand --------------------- Hm, auch das verstehe ich nicht, der Ball kann mit 85° Winkel gegen das Lot zur Torlinie einfallen, dann nützen mir die Randwerte doch nichts oder? Fazit: ------ Ich glaube, dass das Meßsystem zuverlässig funktioniert; das kann kein SW-Bug mehr sein. Leider habe ich keinen Logic-Analyser, um die Kanäle wirklich zuverlässig aufzuzeichnen, der würde ja Klar- heit bringen. Wir werden einmal probieren, den Kicker entsprechend zu modifizieren, heute hatten wir einen Test, indem ich das Tor horizontal mit Papier zugehalten habe, so dass unten nur noch ein 4cm Schlitz übrig blieb. Wenn der Ball das Papier gestriffen hat, weil er zu hoch war, war in der Regel auch die Messung zu klein. Hab ich nichts am Papier gespürt, schien der Meßwert plausibel.
Na gut. Ich lass das jetzt. Ich habe den Eindruck (und ich schreibe dies ohne irgendeinen versteckten Vorwurf), dass meine Überlegungen nicht von Wert für Dich sind. Du musst es halt so machen, wie Du denkst.
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.