Forum: Mikrocontroller und Digitale Elektronik Ringbuffer TI


von Heinz B. (hez)


Lesenswert?

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?

von Heinz B. (hez)


Lesenswert?

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???

von Helmut L. (helmi1)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Heinz B. (hez)


Lesenswert?

>> 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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Heinz B. (hez)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

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.