Forum: Compiler & IDEs avr-gcc spickt Hauptschleife mit cli's


von Christoph S. (mcseven)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Christoph S. (mcseven)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Grrrr (Gast)


Lesenswert?

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.

von Christoph S. (mcseven)


Lesenswert?

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(PCIN­T0_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(PCIN­T0_vect)); 
21
ISR(PCINT3_vect, ISR_ALIASOF(PCIN­T0_vect));
? Ich werd mich nachher mal ans coden machen und dann berichten...

von Christoph S. (mcseven)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.

von Christoph S. (mcseven)


Angehängte Dateien:

Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.
;-)

von Grrrr (Gast)


Lesenswert?

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).

von Christoph S. (mcseven)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.