hello wieder,
mein Empfangs-Ringbuffer arbeitet schon (hoffentlich auch richtig) :)
Jetzt gehts noch um dem Sende-Ringbuffer.
Ich habe mir dazu angeschaut:
http://sdccokr.dl9sec.de/resources.htm#Topic2
"Code for 8051 series UART (interrupt driven, uses timer1)"
Allerdings kommt mir der Code etwas seltsam vor.
Im Interrupt-Handler SerInt wird das Flag is_txing gesetzt oder
gelöscht. Es zeigt an, ob aktuell gesendet wird.
Möchte man etwas verschicken, wird im ser_write_byte T1 gesetzt, wodurch
ein Interrupt ausgelöst und SerInt aufgerufen wird. Dann werden der
Reihe nach die Zeichen rausgeschickt.
Jetzt meine Frage an die Gurus:
Nachdem im ser_write_byte T1 gesetzt wurde vergeht einige Zeit, bis
schließlich is_txing im SerInt gesetzt wird. Was passiert, falls ich
innerhalb dieser Zeit nochmal TI setzen sollte?
Habe mir das jetzt 2 Nächte lang durch den Kopf gehen lassen. Ich
glaube, beim zuvor angeführten Programm gibt es schon ein Problem mit
ser_write_byte.
Diese Funktion schreibt ein Char in den Sende-Ringbuffer. Normalerweise
überträgt man aber nie nur 1 Char. Und sobald man mehr als 1 Char senden
möchte, kann folgendes passieren:
1) main will "ab" verschicken.
Durchlauf 1: 'a' wird ser_write_byte übergeben und verschickt.
2) Jetzt tritt irgendein Interrupt auf, dessen Interrupthandler "xy"
verschicken will.
Durchlauf 1: 'x' wird ser_write_byte übergeben und verschickt.
Durchlauf 2: 'y' wird ser_write_byte übergeben und verschickt.
3) Der Interrupthandler ist fertig, es wird im Hauptprogramm
weitergearbeitet. Es muss noch das 'b' übertragen werden.
Durchlauf 2 (vom main): 'b' wird ser_write_byte übergeben und
verschickt.
Jetzt ist an der Gegenstelle statt "abcd" folgendes angekommen: "axyb".
Also ein Mischmasch.
Irgendwie muss es eine Funktion geben, der man ein Char-Array übergeben
kann. In diesem Char-Array stehen jene Chars, die man übertragen lassen
möchte. Während sich diese Funktion darum kümmert, diese Chars in den
Sende-Ringbuffer zu schreiben, darf sie auf keinem Fall irgendwie
unterbrochen werden.
Aber wie stellt man das an???
Heinz B. schrieb:
> Während sich diese Funktion darum kümmert, diese Chars in den> Sende-Ringbuffer zu schreiben, darf sie auf keinem Fall irgendwie> unterbrochen werden.>> Aber wie stellt man das an???
In dem man vor dem Eintritt in die Funktion die Interrupts sperrt und
danach wieder freigibt.
Helmut Lenzen schrieb:
> Heinz B. schrieb:>> Während sich diese Funktion darum kümmert, diese Chars in den>> Sende-Ringbuffer zu schreiben, darf sie auf keinem Fall irgendwie>> unterbrochen werden.>>>> Aber wie stellt man das an???>> In dem man vor dem Eintritt in die Funktion die Interrupts sperrt und> danach wieder freigibt.
Aber Vorsicht: Ist cool man
Nein, so gehts nicht.
Das Problem: Ist der Sendebuffer fast voll, dann gibt es ein Problem:
Die Interrupts müssen kommen, denn nur sie können dir garantieren, dass
der Buffer durch die Zeichen die erst im Sendeinterrupt tatsächlich
rausgehen, geleert wird.
Die einzige Lösung ist: diszipliniert programmieren.
Eine ISR hat per Konvention nichts zu verschicken. Wenn sie es doch tut,
dann muss man damit leben, dass es Mischmasch geben kann.
Alles andere hat das Potential für einen Deadlock.
>> In dem man vor dem Eintritt in die Funktion die Interrupts sperrt>> und danach wieder freigibt.
Und wenn in dieser Zeitspanne ein Interrupt auftritt? Dann kann der
nicht erfasst und verarbeitet werden, oder?
>> Eine ISR hat per Konvention nichts zu verschicken.
Macht eh keine direkt. Schreibt alles nur in den Sende-Ringbuffer. Und
dazu wird eine Funktion aufgerufen. Und diese Funktion darf nicht
unterbrochen werden, sonst gibts womöglich das Chaos. Sehe ich doch
richtig, oder?
Heinz B. schrieb:
>>> In dem man vor dem Eintritt in die Funktion die Interrupts sperrt>>> und danach wieder freigibt.> Und wenn in dieser Zeitspanne ein Interrupt auftritt? Dann kann der> nicht erfasst und verarbeitet werden, oder?>>>> Eine ISR hat per Konvention nichts zu verschicken.> Macht eh keine direkt. Schreibt alles nur in den Sende-Ringbuffer.
Auch das nicht. Eine ISR hat nichts auszugeben! Egal ob direkt über die
USART oder über einen Ringbuffer.
Wenn sie eine Ausgabe machen will, dann setzt sie maximal ein Flag und
die Ausgabe erfolgt auf regulärem Weg aus der Hauptschleife.
Oder aber: damit leben, dass Ausgaben ineinander verschachtelt sind.
Das ist nun mal das Wesen eines Interrupts: Man weiß nie, wann einer
auftritt.
Interrupts haben 'große Macht'. Aber große Macht bedeutet auch große
Verantwortung.
Heinz B. schrieb:
>>> Eine ISR hat per Konvention nichts zu verschicken.> Macht eh keine direkt. Schreibt alles nur in den Sende-Ringbuffer. Und> dazu wird eine Funktion aufgerufen. Und diese Funktion darf nicht> unterbrochen werden, sonst gibts womöglich das Chaos. Sehe ich doch> richtig, oder?
Doch. Diese Funktion muss sogar unterbrochen werden können.
Vom richtigen Interrupt!
Nämlich dem Interrupt, der dann tatsächlich die Zeichen über die UART
ausgibt.
Ist dein Ringbuffer 20 Zeichen groß, der auszugebende String aber 30
Zeichen lang, dann MUSS dieser Interrupt kommen können. Ansonsten hast
du mit SIcherheit ein Problem. In den Fällen, in denen der auszugebende
String kleiner als der Ringbuffer ist, hängt es davon ab, was bereits
vorher im Ringbuffer war, ob sich das ausgeht oder nicht. Und wenn in
deiner UART Übertragung auch noch Handshake eine Rolle spielt, dann
liegt es sowieso auch nicht mal ansatzweise in deiner Hand, mittels
Interrupt sperren oder freigeben die korrekte Funktion der Ausgabe
sicherzustellen.
>> Interrupts haben 'große Macht'. Aber große Macht bedeutet auch große>> Verantwortung.
Du schreibst ein wenig geschwollen ;)
Na OK. Also darf dann halt nur ein Flag gesetzt werden. Allerdings ist
das schon etwas seltsam. Schließlich verliere ich dadurch den Effekt,
den ich mittels Interrupt erzielen möchte, dass nämlich irgendetwas
spontan passiert.
Ach übrigens. Also ich diesen Interrupt erwähnt hatte, habe ich nicht
den Interrupt für die serielle Schnittstelle gemeint, sondern
irgendeinen anderen. Vielleicht von einem Counter ... oder was weiß ich
...
Heinz B. schrieb:
>>> Interrupts haben 'große Macht'. Aber große Macht bedeutet auch große>>> Verantwortung.> Du schreibst ein wenig geschwollen ;)
Schon klar.
Aber was ich dir vermitteln wollte:
Es gibt Dinge, die kannst du nicht mit Sprachmitteln lösen. Da gibt es
nur eines: Als Programmierer die Disziplin aufbringen, dass man gewisse
Dinge einfach nicht macht. (Oder mit den Folgen davon leben)
> Na OK. Also darf dann halt nur ein Flag gesetzt werden. Allerdings ist> das schon etwas seltsam. Schließlich verliere ich dadurch den Effekt,> den ich mittels Interrupt erzielen möchte, dass nämlich irgendetwas> spontan passiert.
Du hast eine 2-te Restriktion, auf die du aufpassen musst.
Wenn mehrere Funktionen 'gleichzeitig' dieselbe Resource benutzen
wollen, dann muss man Komprmisse eingehen.
Ist im realen Leben auch nicht anders. Teilst du dir mit dem Nachbarn
ein einziges Auto, dann muss man Kompromisse eingehen. Spontan übers
Wochenende wegfahren, wird zwar manchmal funktionieren, manchmal aber
auch zu Konflikten führen.
> Ach übrigens. Also ich diesen Interrupt erwähnt hatte, habe ich nicht> den Interrupt für die serielle Schnittstelle gemeint, sondern> irgendeinen anderen. Vielleicht von einem Counter ... oder was weiß ich> ...
Und wie sperrst du den?
Klar. Man kann in einem Programm Buch führen, welche Interrupts
überhaupt verwendet werden und jeden einzelnen abdrehen bzw. danach
wieder zulassen. Aber ganz abgesehen davon, dass das fehlerträchtig ist,
wird sich dein Programm irgendwann nur noch damit beschäftigen, welche
Interrupts überhaupt benutzt werden, welche zugelassen werden können und
welche nicht.
Ich hab mir nicht alles durchgelesen, aber zur ursprünglichen Frage:
Zeichenausgaberoutine:
- Du mußt zuerst testen, ob noch für ein Byte Platz in der FIFO ist.
Ansonsten wartest Du, bist Platz ist.
- Jetzt kommt ne Interruptsperre und Du legst das Zeichen in die FIFO.
- Dann testest Du ein Bit (tx_busy) und wenn es nicht gesetzt ist, setzt
Du es und TI, damit der Sendeinterrupt anfangen kann.
- Interrupt wieder freigeben.
Sendeinterrupt:
- Du löscht TI.
- Du prüfst, ob noch ein Zeichen im FIFO ist und sendest es.
- Ist kein Zeichen da, löscht Du tx_busy.
Peter
Eine eigene Frage?
Dieser Thread ist über 16 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.