FIFO Buffer in C

Gast
Du legst ein Array vom Typ der zu speichernden Daten an.
Du definierst zwei Zeiger. Einen für die Stelle, an die du
Daten schreibst, einen für die Stelle, wo du die Daten wieder ausliest.
Nach jeder Schreib- oder Leseoperation erhöhst du den jeweiligen
Zeiger. Wenn ein Zeiger über das Ende des Arrays hinausgeht, setzt
du ihn wieder auf Null.

Soweit das mal ganz grob umrissen.
Source Schnippsel:
////////////////////////////////////
#define CON_OUT_BUF_SIZE           64

/* Buffer masks */
#define CON_OUT_MASK               (CON_OUT_BUF_SIZE-1ul)

/* Buffer read / write macros */
#define CON_BUF_RESET(Fifo)      (Fifo->rd_idx = Fifo->wr_idx = 0)
#define CON_BUF_WR(Fifo, dataIn) (Fifo->data[CON_OUT_MASK & 
Fifo->wr_idx++] = (dataIn))
#define CON_BUF_RD(Fifo)         (Fifo->data[CON_OUT_MASK & 
Fifo->rd_idx++])
#define CON_BUF_EMPTY(Fifo)      ((CON_OUT_MASK & Fifo->rd_idx) == 
(CON_OUT_MASK & Fifo->wr_idx))
#define CON_BUF_FULL(Fifo)       ((CON_OUT_MASK & Fifo->rd_idx) == 
(CON_OUT_MASK & Fifo->wr_idx+1))
#define CON_BUF_COUNT(Fifo)      ((CON_OUT_MASK & Fifo.wr_idx) - 
(CON_OUT_MASK & Fifo.rd_idx))

// Serial output buffer
typedef struct {
  unsigned char data[CON_OUT_BUF_SIZE];
  unsigned int wr_idx;
  unsigned int rd_idx;
} CON_Fifo_t;
Klaus wrote:
> Wo werden denn hier die Pointer definiert? Welches ist der Pointer?

Wozu brauchst du einen Pointer?
Andread arbeitet auf einem ganz normalen Array und nimmt
Inidizes um in das Array zuzugreifen.
Ist doch völlig ausreichend.
Stefan Ernst wrote:
> Vielleicht sollte man noch dazu schreiben, dass CON_OUT_BUF_SIZE
> zwingend eine Zweierpotenz sein muss.

Ja.
1
#if((CON_OUT_BUF_SIZE|CON_OUT_MASK) != (CON_OUT_BUF_SIZE+CON_OUT_MASK))
2
#error bad CON_OUT_BUF_SIZE !!!
3
#endif


Peter
Gast
Hi

>Du legst ein Array vom Typ der zu speichernden Daten an.
>Du definierst zwei Zeiger. Einen für die Stelle, an die du
>Daten schreibst, einen für die Stelle, wo du die Daten wieder ausliest.
>Nach jeder Schreib- oder Leseoperation erhöhst du den jeweiligen...

Das ist ein Ringpuffer und kein FIFO. Da reicht ein Zeiger, der nach dem 
Schreiben um 1 erhöht und vor dem Lesen um 1 erniedrigt wird.

MfG Spess
Ich hab das mal mit Maske probiert, spart tatsächlich einige Bytes Code.

Blöd ist nur, daß auch der SRAM in 2-er Potenzen organisiert ist.

Wenn ich also nen Puffer beim ATtiny45 einrichte und 18 Byte für Stack 
und Variablen reserviere, kann ich nur 64 Byte Puffer einrichten.

Ich bleibe daher beim Vergleichen und 0 setzen, dann kann ich sogar 110 
Byte Puffer nehmen, also fast das doppelte!


Peter
Gast
> #define CON_BUF_COUNT(Fifo)      ((CON_OUT_MASK & Fifo.wr_idx) -
> (CON_OUT_MASK & Fifo.rd_idx))

So funktioniert es auch wenn wr_idx < rd_idx ist:

#define CON_BUF_COUNT(Fifo)  ((Fifo.wr_idx - Fifo.rd_idx) & 
CON_OUT_MASK)
Gast
Man könnte ja anstatt

CON_OUT_MASK & Fifo->wr_idx++
auch
(Fifo->wr_idx++) % CON_OUT_BUF_SIZE

verwenden und hätte damit das Problem der auf Zweierpotenzen 
eingeschränkten Puffergröße umgangen. Sollte auch nicht mehr
Platz im .text Segment brauchen und auch nicht langsamer sein.

Viele Grüße,
 Martin L.
Gast
Ist ja nur ein 8-Bit Modulo notwendig. Aber stimmt - der Atmel hat 
keinen "mod"-Befehl. (Übrigens brauchen bessere Prozessoren bei 16 oder 
32bit Berechnungen deren höherwertigen Bits null sind nicht länger als 
mit entsprechend kleineren Registern.)

Viele Grüße,
 Martin L.
Gast
Nur wenige Controller haben einen div- oder mod Befehl. Mir fällt
da nur der Cortex M3 ein. Das traurige am C Standard ist das
solche Berechnungen immer mit mindestens 16Bit durchgeführt werden.
Vielleicht optimiert der AVR-GCC das in diesem Fall und rechnet
tatsächlich nur mit 8 Bits. Bei einer Multiplikation macht er das
jedenfalls nicht. Da werden 8Bit Operanden auf 16Bit aufgebohrt und
das Ergebnis wieder auf 8Bit zurückgestutzt.

>(Übrigens brauchen bessere Prozessoren bei 16 oder
>32bit Berechnungen deren höherwertigen Bits null sind nicht länger als
>mit entsprechend kleineren Registern.)

Ich nehme an du meinst 16/32 Bit Prozessoren?
Interessanterweise dauert es auf solchen Prozessoren mitunter sogar
länger wenn mit kleinen Datentypen gerechnet werden soll.
Da werden nämlich Ergebnisse die ja in z.B. 32Bit Registern stehen
erst mit Masken verknüpft um die oberen Bits auszublenden.
Deshalb gibt es in der <stdint.h> die 'fast' Typen.
Also z.B. uint_fast8_t der bei einem ARM7 als 'unsigned long'
definiert ist.
Ergo: Die kleinsten Datentypen sind nicht immer die Besten.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 18 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.