mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik MCU zu langsam für mehrere Interrupts?


Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: thkaiser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das kann vieles sein:
Seiteneffekte durch mehrfach genutzte Register (passiert schon mal , daß 
man ein Register nicht pusht/popt)
Tatsächliche Timing-Probleme - dann könnte es sein, daß ein 
Register-Inhalt überschrieben wird, bevor der vorhergehende fertig 
gesendet ist.
Oftmals hilft da Optimierung oder ein Umdenken.
Ich hatte beispielsweise das Problem, daß eine I²C-Routine im 
Zusammenhang mit Software-PWM nicht mehr so richtig wollte. Ein wenig 
umschreiben, eine Art von Interrupt-Priorisierung und schon klappte 
es....
Evtl. die Interruptroutinen kürzen und mehr ins Hauptprogramm.
Ohne mehr Info ist es allerdings schwer, weitergehende Vorschläge zu 
machen.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bist du sicher dass du ein Softwareproblem hast? "rotierender Trafo" 
hört sich jedenfalls schon mal ziemlich abenteuerlich an. Funktioniert 
die Übertragung fehlerfrei wenn die anderen Interrupts deaktiviert sind?

Wenn ein Interrupt auftritt wird das entsprechende Statusbit gesetzt, 
die Abarbeitung erfolgt dann sobald eine evtl. gerade laufende 
Interruptroutine wieder beendet ist. Die Priorität der Abarbeitung 
entspricht der Reihenfolge der Interruptvektoren im Datenblatt. 
"Verloren" geht in Int nur dann, wenn schon ein anderer des gleichen 
Typs in der Warteschlange sitzt. Deshalb ist es grundsätzlich wichtig, 
die Interruptroutinen möglichst kurz zu halten, damit erst gar keine 
längeren "Warteschlangen" entstehen.

Wenn man einen selten auftretenden Interrupt mit langer Bearbeitungszeit 
hat und einen der sehr oft auftritt, dann kann man bei dem langsamen am 
Anfang der Int.routine mit "sei" die Interrupts wieder aktivieren, so 
dass die Routine durch den schnellen Interrupt unterbrochen werden kann.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: thkaiser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In so zeitkritischen Geschichten sollte man vielleicht kein C verwenden 
- manchmal gehts halt doch nicht ohne Assembler.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich denke auch, daß da irgendein Interrupt zu lange dauert. In C sieht 
man eben nicht mehr, wie teuer in Bezug auf Rechenzeit bestimmte 
Instruktionen sind.

Beim 8051 ist das auch leichter zu entschärfen, da man dort 2-4 
verschiedene Prioritätsstufen vergeben kann. D.h. ein schneller 
Interrupt kann einen langsamen unterbrechen ohne das es Probleme gibt.

Beim AVR muß man das simulieren, indem ein langsamer Interrupt zuerst 
sich selber sperrt, damit er sich nicht versehentlich wieder selbst 
aufruft und dann die Interupts global wieder freigegeben werden. Wenn er 
dann durch eine weiteren langsamen unterbrochen werden könnte, muß 
dieser es genau so machen.
Vor dem Verlassen des Interrupts muß dann alles wieder rückgängig 
gemacht werden.


Zu langsam dürfte Deine CPU aber in keinem Fall sein.
Vielleicht kannst Du ja Deinen Kode als Anlage anhängen, damit man mal 
sehen kann wo die Rechenleistung verpulvert wird.


Peter

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ein paar einfache Tips, um die ISRs schneller zu machen:
-reserviere eine char Variable im C Programm mit fester Adresse
unsigned char sreg_bak @R3;
gibt zwar ne Warnung beim Compilerlauf (never used bla bla), ignorieren.
Des weiteren schaust du dir das erzeugte Assembleristing genau an und 
sicherst nur die Register, die wirklich verändert werden, der Compiler 
geht meist mit dem groben Besen drüber und sichert alle allgemeinen 
Register, auch wenn du in der ISR nur 2 benutzt. Das ist doppelt 
ärgerlich, da natürlich alle gesicherten Register wieder gepopt werden, 
dazu kommt je ein call für die Sicherungs- bzw. 
Wiederherstellungsroutine.
Beispiel: du siehst im Listing, daß nur R10, R11, R30,R31 benutzt 
werden.
Deine ISR sieht dann so aus:
#pragma savereg-   //nicht automatisch sichern
void interrupt isr1 (void) {
#asm
 in R3, sreg
 push r10
 push r11
 push r30
 push r31
#endasm
das eigentliche ISR in C
#asm
 pop r31
 pop r30
 pop r11
 pop r10
 out sreg, r3
#endasm
}
ist natürlich nur ein Beispiel, wie es exakt realisiert wird, hängt vom 
Compiler und deinem Programm ab. sreg_bak kannst du in allen ISR 
benutzen, wenn du keine verschachtelten Interrupts benutzt.
Ansonsten die allgemeinen Regeln beachten, möglichst kurze Laufzeit der 
ISR, nur das Ereignis in irgendeiner Art speichern, die Verarbeitung 
dann im Hauptprogramm.

Autor: geloescht (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, die Division int/3 dürfte am meisten CPU-Zeit fressen. Divisionen 
sind mit Abstand das teuerste.

Auch kann man im Compare-Modus den Timer automatisch löschen lassen, 
dann muß man nicht immer OCR1 neu laden.


Peter

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
und die Display-routine würde ich auch aus der ISR verbannen.
In der ISR:
display_ready=1;  //Bitvariable


Im Hauptprogramm dann:
 while (!display_ready);
 display_ready=0;
 display();

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die 2. Nutzung von T1 hatte ich übersehen, dann ist es ohne "clear on 
compare" besser.


Woher kommt denn diese magische 378 ?
Ich würde ja durch 60 teilen (60 Minuten).
Die Division sollte man dann aber im main() machen, ist ja egal, wann 
die fertig ist.

Wozu ist denn das Lap gut ?


Und in Interrupts nur das static bzw. global, wo der Wert auch wirklich 
das nächste mal bzw. woanders gebraucht wird.
Kostet dich ja jedesmal 8 Byte Flash, 6 Zyklen je char variable (=16,12 
je int).


Peter

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich hätte hier noch nen Vorschlag, das Ganze einfach als PLL zu machen.
Der T1 arbeitet mit "clear on compare" und der Wert von OCR1 wird 
solange nachgeführt, bis der Synchronimpuls genau in der Mitte des 
letzen Displaypunktes display_mem[59] erfolgt.

Die Daten für alle 60 Zeigerstellungen werden in display_mem[] abgelegt, 
das kann z.B. in aller Ruhe im main erfolgen.

Je nachdem, wie genau man den Startwert von OCR1 setzt, dreht sich die 
Uhr nach dem Einschalten, bis sie in der richtigen Position einrastet.


Peter

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Kalle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Guido,

Frage: wo hast du die Schaltung/Platine/Anleitung zu dem tollen Projekt 
den her. Sieht toll aus....

mfg Karl

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Guido,

bei 8MHz, 50U/s und 120 Pixel dauert 1 Pixel 1333 Takte.
Die Änderung von OCR1 wirkt sich ja 120-fach aus, d.h. +/-120 Takte.

Soltest Du allerdings einen Vorteiler 1/8 nehmen, ist ein Pixel nur 167 
T1-Takte lang und dann sind 120 Takte schon sehr viel.

Man kann aber auch noch feiner synchronisieren, indem man immer ab einer 
bestimmten Position OCR1 um 1 verringert und am Nullpunkt wieder erhöht.
Diese Position wird dann im Synchronisationsinterrupt hoch- bzw. 
runtergezählt und bei >=120 bzw. <0 wird erst OCR1 geändert.


Peter

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Autor: geloescht (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieser Beitrag wurde auf Wunsch des Autors geloescht.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.