Forum: Mikrocontroller und Digitale Elektronik Ringspeicher - wie am besten realisieren?


von Ralf (Gast)


Lesenswert?

Hallo,

ich habe bis jetzt meine serielle Schnittstelle (8051er) immer über
Polling bedient. Jetzt wird es auch mal Zeit, mich mit Ringspeichern zu
beschäftigen. Leider habe ich bis jetzt noch keinen (befriedigenden)
eigenen Ansatz gefunden, der wirklich gut wäre.

Kann mir jemand dabei helfen?

Das ganze sollte so ausgelegt sein, dass ich die Ringspeicher-Routinen
beliebig verwenden kann, also entkoppelt von den seriellen Routinen,
weil diese auch mit Handshake usw. arbeiten.

Sprache wäre Assembler oder C.

Google-Suche oder Forums-Suche hat nicht geholfen. Wahrscheinlich die
falschen Stichwörter verwendet ;-)

Gruß Ralf

von Rufus T. Firefly (Gast)


Lesenswert?

Nach dem englischen Begriff "FIFO" (first-in, first-out) suchen könnte
helfen.

von Mike (Gast)


Lesenswert?

Oder die Forensuche, diese Fragestellung gab es schon öfters und
natürlich auch qualifizierte Antworten. Der Informatikskript jeder
beliebigen Uni sollte so etwas im Normalfall auch enthalten, genauso
wie jedes gute C-Buch.

von Markus_8051 (Gast)


Lesenswert?

Ich habe zu diesem Thema eine kleine Sammlung von drei Upros in
8051-Assembler geschrieben:
buf_init, buf_put, buf_get
Sie verwalten einen 16-Bytes großen Ringspeicher. (Die Größe läßt sich
in 2^n-Schritten leicht anpassen). Außerdem werden die Bit-Variablen
buf_full und buf_empty zur Verfügung gestellt.

Bei Interesse kann ich den Code heute abend hier posten.

Gruß,
Markus_8051

von Ralf (Gast)


Lesenswert?

Danke für die Antworten.

@Markus_8051:

Ja, bitte poste mal.
Wie ist das, ich muss aber (was mir erst hinterher auffiel) für jeden
RingBuffer, den ich im Projekt verwende, eigene Zugriffsroutinen
verwenden, richtig?

Gruß Ralf

von Matthias (Gast)


Lesenswert?

Hi

nö. Du kannst jeder Funktion ja einen Pointer auf eine
struct ringbuf
{
   ...
}
mitgeben.

Matthias

von Markus_8051 (Gast)


Lesenswert?

Hmmm, in C geht das wohl so, in Assembler wird das schwieriger. Da ich
nur einen Ringbuffer verwende, habe ich mir da auch keine weiteren
Gedanken zu gemacht.

Nun ja, vielleicht kannst Du meinen Code ja noch verbessern.

Wie gesagt, kommt heute abend!!!

Markus_8051

von Ralf (Gast)


Lesenswert?

@Matthias:

Pointer auf eine Struktur... Nur um mich zu vergewissern, was du
meinst:

Ich lege also eine Struktur an, die dann jeweils den Ringbuffer und die
Schreib-Lesepointer enthält? Hört sich gut an (wenn ich es wirklich so
verstanden habe, wie du meinst ;-)

Wie ist das, in einigen Codes, die ich gefunden habe, wird noch eine
Variable verwendet, die aussagt, wieviele ungelesene Bytes sich im
Ringbuffer befinden. Bringt die einen Vorteil, den ich nicht sehe? Denn
die Angabe, wieviel ungelesenes noch drin ist, bekomme ich doch auch,
indem ich die Differenz aus Schreib- und Lesepointer bilde, oder?
Latürnich nur unter der Vorraussetzung, dass kein Überlauf
stattgefunden hat. In dem Fall wäre aber der Buffer-Inhalt eh Gülle,
also brauche ich die Variable nicht wirklich, oder?

Gruß Ralf

PS: Warst du nicht zusammen mit Stefan Schöndienst in der Berufsschule?

von Matthias (Gast)


Lesenswert?

Hi

so war das gedacht. Die zusätzliche Variable die die Menge der Zeichen
im Buffer angiebt ist nicht wirklich nötig und bringt auch keine
sonderlichen Performancegewinne wenn man nicht 10000mal pro empfangen
Zeichen den Puffer prüft.

PS: Jap. Sag ihm mal nen schönen Gruß von mir.

Matthias

von Dirk (Gast)


Lesenswert?

Hallo,

ich hatte mal einen µkernel für einen 8051 geschrieben in den ich einen
256Byte großen ringpuffer für den empfang realisiert hatte in ASM,
256Byte weil es genügent groß ist und es sich wunderbar über movx
a,@DPTR adressieren wenn der puffer an einer 256byte grenze anfäng wie
z.b. 0x0400h oder so, da kann man einfach DPL durch einen
VariablenPointer ersetzen. Man brauch drei variablen um diesen sinnvoll
und effektiv zu erstellen.

1. ByteCounter, diese variable beinhalt die bytes die sich im
ringpuffer befinden.
2. WritePointer, dieser zeigt auf die nächste Speicherzelle die
geschrieben wird, beim schreiben wird WritePointer um 1 erhöht und
ByteCounter auch
1. ReadPointer, dieser zeigt auf das nächste byte welches gelesen wird,
beim lesen wird ReadPointer um 1 erhöht und ByteCounter um 1
verringert.

Die drei variablen sind bytevariablen wobei Write und ReadPointer auch
überlaufen dürfen, weil sie trotzden auf die richtige speicherzelle
zeigt -> Ringbuffer. Wichtig ist nur das Bytecounter nicht überläuft,
sonst ist der puffer voll. Wenn Bytecounter auf 255 steht kann man
einfach neue byte für den puffer ignorieren. Wenn Bytecounter auf null
steht ist der puffer leer und man wartet aufs nächste byte.

ich hoffe das hilft dir ein bischen.

CA

von Ralf (Gast)


Lesenswert?

@Matthias:

Okay, hab mir das jetzt durchdacht. Was mir aufgefallen ist:

1. Wenn ich die Sache mit Strukturen mache, müsste ich mehrere
Strukturen verwenden, wenn ich z.B. einen Ringbuffer mit 16 Bytes und
einen mit 32 Bytes haben möchte, richtig?

Also etwa so:

typedef struct {
  uchar ucBuffer[16],
  uchar ucRead;
  uchar ucWrite;
} stRingBuffer_TYP1;

typedef struct {
  uchar ucBuffer[32],
  uchar ucRead;
  uchar ucWrite;
} stRingBuffer_TYP2;

2. Soll ich den Zugriff auf das Array über den Index des Array-Feldes
machen, oder lieber über einen Pointer?

@Dirk:

Danke, das ist auch hilfreich.

Gruß Ralf

von Ralf (Gast)


Lesenswert?

Nachtrag zum Beitrag von gerade eben:

Kann ich mit einer Funktion auf beide Struct-Typen zugreifen? Und wie
deklariere ich überhaupt einen Pointer auf eine Struktur?!?

Gruß Ralf

von Matthias (Gast)


Lesenswert?

Hi

zu 1.
Eigentlich ja.

zu 2.
Eigentlich egal. Du kannst dir aber mal im Detail ankucken was der
Compiler daraus macht wenns wirklich hocheffizient sein soll. Der
Zugriff mittels Array-Index ist zwar (programmiertechnisch) einfacher
aber unter Umständen langsamer.

>Kann ich mit einer Funktion auf beide Struct-Typen zugreifen?
Eigentlich nicht. Mit einer gezielten Anordnung der Strukturvariablen
geht das aber. Ist aber unsauber und AFAIK auch unportabel.

Grundsätzlich und sauberer würde man sowas mit einem Pointer in der
Struktur auf den gewünschten Datentyp undstatisch bzw. dynamisch (z.B.
per malloc) allokiertem Speicher erledigen. In der Struktur muß dann
zusätzlich noch die Länge verwaltet werden. Also etwa so:

uchar ringBufMem1[16];
uchar ringBufMem2[32];
typedef struct {
  uchar * ucBuffer;
  uchar ucRead;
  uchar ucWrite;
  uint length;
} stRingBuffer;

stRingBuffer buf1;
stRingBuffer buf2;

void ringBufInit(void)
{
  buf1.ucBuffer = ringBufMem1;
  buf1.length = sizeof(ringBufMem1)/sizeof(uchar);
  buf2.ucBuffer = ringBufMem2;
  buf2.length = sizeof(ringBufMem2)/sizeof(uchar);
}

Ob sowas auf einem kleinen µC nötig ist mußt du selbst entscheiden.

>Und wie deklariere ich überhaupt einen Pointer auf eine Struktur?!?

stRingBuffer * pTostRingBuffer;

Matthias

von Ralf (Gast)


Lesenswert?

Hi Matthias,

> Ob sowas auf einem kleinen µC nötig ist mußt du selbst entscheiden.

Der Gedanke war, falls ich mal in einem Projekt mit mehreren
Ringspeichern arbeite, dass ich die Ringspeicher trotz
unterschiedlicher Größe mit der gleichen Routine bearbeiten kann. Ich
habe es jetzt so gemacht, dass ich eben bis zu drei Ringspeicher
anlegen kann, und jeder Ringspeicher hat seine eigenen Routinen.

Aber mal sehen, dein Code-Beispiel hat glaube ich (nach meinem
Verständnis) das Potential für meine Anwendung.

Was tut man nicht alles, um C zu lernen grins

Ein Gedanke von mir war auch, "irgendwie" den Typ der im Pointer
übergebenen Struktur herauszufinden, vielleicht hätte sich damit was
machen lassen.

Aber so wie ich die Sache verstanden habe (dank deiner Hilfe mit der
Pointer-Deklaration), verwendet C für den Zugriff auf die Elemente der
Struktur feste Offsets, die zur Startadresse der Struktur hinzuaddiert
werden.

Gruß Ralf

von Edgar Renner (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

hat noch jemand Lust auf Assembler?

Im upload findet Ihr ein paar Code-Fetzen zum gepufferten Lesen und
Schreiben der RS232-Schnittstelle im 8051. Bitte beachtet folgende
Anmerkungen:

1. Die Größe der beiden Puffer kann frei gewählt werden. Da bei jedoch
beide im internen RAM liegen sollte man geizen.

2. Es erfolgt keine Überwachung des Überlaufs.

3. Ich verwende als Assembler AS von Alfred Arnold, der in einer
selbstentwickelten IDE eingebettet ist. Dabei ersetzt die IDE alle
Befehle, die mit Unterstrich „_“ beginnen in gültige
Assembler-Statements. Die _if statements funktionieren so wie man
meint, das sie funktionieren. Im folgenden Posting hängt die Quelle wie
sie der Assembler sieht, d.h. mit ersetzten _if's


cu

von Edgar Renner (Gast)


Angehängte Dateien:

Lesenswert?

... und nun der Anhang

von Edgar Renner (Gast)


Angehängte Dateien:

Lesenswert?

... hups ... das war eine alte Version :-)

von Markus_8051 (Gast)


Angehängte Dateien:

Lesenswert?

So, hier kommt mein inc-File für einen Ringbuffer für die serielle
Schnittstelle.

Ich habe ihn nur zum senden benutzt. Beachte bitte, daß die Prozedur
send_char erwartet, daß es eine IRQ-basierte Senderoutine gibt, die den
Buffer leert, wenn die RS232 frei ist. Sonst hängt sich der µC hier
weg.

Viel Erfolg damit,

Markus_8051

von Ralf (Gast)


Lesenswert?

Noch ne Frage wegen Pointer auf Strukturen:

Kann ich anstatt einen Pointer auf die Struktur zu übergeben, auch den
Pointer auf ein Element der Struktur übergeben?

Gruß Ralf

von Ralf (Gast)


Lesenswert?

@Markus_8051 & Edgar Renner:

Danke für die Routinen, werd ich mir auch mal angucken.

Gruß Ralf

von Matthias (Gast)


Lesenswert?

Hi

im Prinzip ja. Du kannst aber aus einem Pointer auf ein Element der
Struktur eigentlich nicht mehr auf die Struktur selber zurückschließen
wenn es portabel sein soll.

Matthias

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.