Erweiterungen
Schön wäre es, wenn man auch einen FIFO-Überlauf oder HW-Fehler, wie Framing Error oder Overrun, melden würde. Habe das für FIFO-Überlauf mal gemacht.
Hier meine Erweiterungen zum Code:
fifo.h
enum { Fifo_Overrun = 1 }; typedef struct { unsigned char volatile count; // # Zeichen im Puffer unsigned char size; // Puffer-Größe unsigned char *pread; // Lesezeiger unsigned char *pwrite; // Schreibzeiger unsigned char read2end, write2end; // # Zeichen bis zum Überlauf Lese-/Schreibzeiger unsigned char state; } fifo_t; static inline uint8_t _inline_fifo_put (fifo_t *f, const uint8_t data) { unsigned char write2end; unsigned char* pwrite; unsigned char cRet; cRet = 0; if (f->count >= f->size) { f->state = Fifo_Overrun; cRet = 1; goto end; } ... end: return cRet; } static inline uint8_t _inline_fifo_get (fifo_t *f) { unsigned char* pread; unsigned char data; unsigned char read2end; if (f->state) return -2; if (!f->count) return -1; ... }
Dann noch eine Erweiterung. Ich lese nur ganze Zeilen von der seriellen Schnittstelle. Also fand ich es gut eine Funktion zu haben, die gleich im Fifo nach schaut, ob eine Zeile vorhanden ist.
fifo.c
int fifo_gets (fifo_t *f, char *buf, int *len ) { unsigned char* pread; unsigned char count; unsigned char idx; int result; unsigned char read2end; if (f->state) return -2; if (!f->count) return -1; pread = f->pread; count = f->count; read2end = f->read2end; result = 1; idx = 0; while (count != 0) { count--; if (read2end-- == 0) { read2end = f->size-1; pread -= f->size; } buf[idx] = *(pread++); if (buf[idx] == '\n' || buf[idx] == '\r') { result = 0; break; } idx++; } /* Wenn ein \r\n gesendet wird ist beim ersten Lesen eine ganze Zeile * in buf. Beim zweiten Lesen nur 1 Zeichen. In der höherliegenden Logik, * wird immer eine sinnvolle komplette Zeile erwartet. * Deshalb wird eine "Zeile" mit nur einem Zeichen ignoriert. */ if ((result == 0) && (idx > 1)) { f->pread = pread; f->read2end = read2end; f->count = count; *len = idx; buf[idx] = '\0'; } else { buf[0] = '\0'; *len = 0; } return result; }
int fifo_get_nowait (fifo_t *b)
Liefert das nächste Byte aus der FIFO als int bzw. -1, falls die FIFO leer ist.
Was machst du, wenn FF gespeichert ist ? oder muß das nur für Text gehen ? --PicNick 18:16, 4. Mär 2006 (CET)
Wenn FF gespeichert ist, wird FF zurück geliefert. Die FIFOs speichern ihre Werte als unsigned. Der return-Wert ist int, mithin ist also 255 != -1. Will man ein char lesen, dann so:
char zeichen; int value; if (value = fifo_get_nowait (&fifo), value >= 0) zeichen = (char) value;
bzw. man schreibt value direkt nach UDR oder wohin auch immer. --SprinterSB 19:04, 4. Mär 2006 (CET)
d.h. du verwendest die Bits >8 implizit als Schalter ? naja. --PicNick 08:59, 5. Mär 2006 (CET)
In der FIFO stehen ja nur Werte >= 0. Und der Rückgabewert ist auch immer >= 0, wenn was drinne ist, ansonsten eben <0. Ich erweitere also den Wertebereich. Ausserdem kann auf <0 sehr effizient getestet werden. Wie würdest du es machen? --SprinterSB 09:59, 5. Mär 2006 (CET)
Mach nur, die Geschmäcker sind halt verschieden, absolute Wahrheiten gibt's nicht. Ich trenne immer Frage und Abholen, wobei die Frage immer non-destruktiv nach der verfügbaren Länge ist.
while (fifo_data() >= required_length ) // anzahl im Buffer { Uns_16 = fifo_read_short() // oder ähnlich }
Beispiel: Bei IP etc. hab immer 16 Bit als Länge vorn, da lese ich z.B. erst, wenn die auch da sind. Und dann erst weiter, wenn der record komplett ist. Aber für eine Controller-UART etc. ist das vielleicht auch etwas über-drüber. --PicNick 12:05, 5. Mär 2006 (CET)
Jo, das geht auch. Auf fifo.count kann man ja frei zugreifen. Beim nachfolgenden Lesen sind aber evtl. mehr Zeichen im Puffer, was bei den meisten Anwendungen nicht stört. Es sollte nur ein einfaches Grundgerüst sein, das man nach eigenem Gusto erweitern kann, etwa
static inline int fifo_contains_at_least_n_bytes_p (const fifo_t * f, const unsigned char num) { f->count >= num; }
Noch praktischer wäre natürlich eine FIFO, in der man ganze Objekte speichern kann, und nicht nur einzelne Bytes... --SprinterSB 22:32, 5. Mär 2006 (CET)
- g* Wenn du Objecte auch als solches reinkriegst, is besser, du legst sie gleich als verkettete Liste an. das ist natürlich mit Heap einiges einfacher. Für Controller wird's da aber eng. geht meist eh' nur ums UART-buffern.
--PicNick 07:59, 6. Mär 2006 (CET)
habe ich hier ein sei(); übersehen ?
Hallo Sprinter :-)
Ich analysiere gerade deine FIFO Puffer, klasse !
Nur eine Frage stellt sich noch, hier
uint8_t sreg = SREG; cli(); f->count--; SREG = sreg;
Muß da nicht noch ein sei(); hin
Oder hab ich da was übersehen ?
Gruß Sebastian
--izaseba 21:34, 23. Apr 2007 (CEST)
- Hi Sebastian. Durch die letzte Anweisung wird das SREG und insbesondere das darin befindliche I-Flag wieder so hergestellt, wie es ursprünglich war. Wenn der Code immer in einem Kontext ausgeführt wird, in dem Interrupts global aktiviert sind, dann geht es mit einer CLI/SEI-Klammer natürlich etwas simpler. --SprinterSB 12:53, 12. Mai 2007 (CEST)