www.mikrocontroller.net

Forum: Compiler & IDEs USART / ISR - Problem mit Timing


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

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei ein zeitkritisches Programm zur PID-Regelung
mittels Mega32 zu erstellen. Zu Testzwecken wollte ich jetzt eine ISR
bei Timer Overflow ausführen lassen und mittels USART prüfen, ob auch
das Timing so ist wie ichs mir vorgestellt habe. Zugleich lasse ich
eine LED blinken.

Problem: wenn ich den USART-Teil in der ISR auskommentiere (weglasse)
blinkt die LED schneller als wenn ich den USART-Teil drin lasse. Ich
vermute die Abarbeitung dieses Abschnitts braucht so lange dass die
nächste ISR-Ausführung verzögert wird? (Wenn ich z. B. 20 Interrupts
pro Sekunde anstatt 1000 mache blinkt die LED nämlich immer gleich
schnell...)

Gibt es denn eine Möglichkeit integers über USART auszugeben ohne die
Verrenkungen die ich dafür mache? Die scheinen ja ziemlich Rechenzeit
zu kosten?! Ziel soll im Endeffekt sein dass die ISR fix und
gleichmässig aufgerufen wird und ich trotzdem darin Integers per USART
auslesen kann.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich vermute die Abarbeitung dieses Abschnitts braucht so lange dass
> die nächste ISR-Ausführung verzögert wird?

Ja klar, das geht so nicht.  Du wartest brav in den
UART-Senderoutinen, bis das jeweils vorhergehende Byte geschrieben
ist.  Bei 9600 Bd rechnet man mit rund 1 ms pro Zeichen.  Du willst
aber N Zeichen ausgeben, 5 <= N <= 13.  Das Ganze soll dann noch 1000
Mal pro Sekunde passieren -- das kann nicht funktionieren, egal wie
du's drehst.

Ansonsten der übliche Ratschlag: ISRs kurz halten.  Nicht nur die
UART-Routinen gehören da nicht rein, auch itoa() dauert viel zu lange
(es muss ja mehrere Divisionen ausführen).  In der Regel setzt man
dort nur ein Flag, ermittelt vielleicht noch eine Timestamp (wobei das
beim compare match Interrupt ziemlich wertfrei ist, findest du
nicht?), und lässt den Rest im Hauptprogramm abarbeiten.

Die Dualität von Zeigern und Feldern hast du auch noch nicht
verstanden, sonst könntest du myCharPtr nämlich gleich komplett
einsparen.  Es macht den Code weder schneller noch verständlicher.

Autor: Roland Praml (pram)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Uli, hier hast ja eine "busy-waiting" Schleife drin:
--
void UART_transmit_string(uint8_t *string){
    while(!(UCSRA & (1<<UDRE)));
   while( *string){ UART_transmit (*string++); }
}
--
d.H. während er auf UDRE wartet treten weitere Interrupts auf (welche
aber in der ISR abgeschaltet sind). Es könnte evtl funktionieren,
wenn du beim Eintritt in den Timer mit sei() die Ints wieder freigibst.
Wahrscheinlich unterbricht sich aber die Timerroutine dann selber und
das ganze Prog. geht hops.

Besser wäre es, eine ebenfalls interruptgesteuerte Programmierung
vorzunehmen. Kurzes Prinzip:
Die zu sendenden Daten in einen Fifo schieben und wenn die UART grad
nix zu zenden hat, das erste Zeichen senden (NUR das erste).
Wenn das Zeichen gesendet wurde, tritt der RX-Complete Interrupt auf,
welcher sich das nächste Zeichen aus der Fifo holt.

Evtl findest aber auch eine fertige Lib die dir die Arbeit abnimmt.

Gruß
Roland

Autor: Uli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Jörg,

danke für die Erklärung, die macht sehr plausibel was ich vermutet
habe!

Ich will während der Regelung gewisse Variablen incl. Timestamp
auslesen - sollte ich das also anstatt in der ISR kontinuierlich in der
Endlosschleife des Hauptprogrammes zu machen? Geht das konform mit der
ISR wenn ich im Hauptprogramm zeitintensive USART-Geschichten mache?
Wird z. B. itoa() dann einfach von der ISR unterbrochen, d. h. habe ich
die Sicherheit dass eine kompakte ISR wirklich gleichmässig abläuft auch
wenn in der Endlosschleife des Hauptprogramms aufwändige Befehle
abzuarbeiten sind?

Klar, eigentlich sollte der Timestamp beim compare match Interrupt
sinnlos sein. Aber durch die USART-Verzögerungen scheint das ganze ja
doch so aus dem Takt zu geraten, dass ich immer unterschiedliche Werte
erhalte, also ergibt es ja in diesem Fall doch eine Aussage! (Wobei mir
nicht einleuchtet wieso ich immer unterschiedliche Time Stamps erhalte -
das sollte doch dennoch der gleiche Wert sein, nur dass durch die
Verzögerungen halt ein paar Timer-Runden ausgesetzt werden und keine
ISR starten, dann aber wieder beim nächsten Overflow?)

Danke für weitere Erklärungen!

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"habe ich die Sicherheit dass eine kompakte ISR wirklich gleichmässig
abläuft auch wenn in der Endlosschleife des Hauptprogramms aufwändige
Befehle abzuarbeiten sind?"


Genau das ist der Sinn von ISRs.

Du darfst bloß nicht elendlangen Code im Main unter Interruptsperre
haben.

Interruptsperren >100 Zyklen sollte man nochmal überdenken (sind
meistens unnötig).


Peter

Autor: Uli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Roland,

danke! Ich denke auch mit sei() ISRs während der USART-Geschichte
zuzulassen klappt wohl eher nicht, dann wird die Übertragung ja
andauernd unterbrochen so dass nicht viel ankommt!

Der Vorschlag mit dem FIFO hört sich recht logisch an. Aber ginge es
evtl. einfacher (so wie oben geschrieben) alle USART-Kommunikation ohne
ISR mit Timestamps in der Hauptschleife durchzuführen?

Merci!

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>sollte ich das also anstatt in der ISR kontinuierlich in der
>Endlosschleife des Hauptprogrammes zu machen
du hast es erfasst
>Geht das konform mit der ISR wenn ich im Hauptprogramm zeitintensive
>USART-Geschichten mache
da ist nichts zeitintensives wenn du die UART per Interrupt bedienst

Interrupt heisst Unterbrechung, wenn diese klein ist merkst du es
nicht, Lies Dir das bitte mal in einem Tutorial oder Buch durch damit
du begreifst was du zu beachten hast

Deine Regelung muss ein feste Zeitpunkte/Samples haben, darauf stellst
du ja die Regelparameter ein.
Dein Programm sollte ungefähr so aussehen

ISR UARTEmpfang
{übernimm Zeichen in UARTpuffer}

ISR Timer wird für Zeitraster Regelung aufgerufen
{Regelzeitpunkt erreicht=TRUE}

main
{
...
1.neue Sollwerte einlesen(z.B. aus UARTpuffer)
2.wenn Regelzeitpunkt erreicht->
Regelroutine->Regelzeitpunkterreicht=FALSE
3. zu 1.
}

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.