Forum: Compiler & IDEs Frage zu Ringpuffer


von Robert (Gast)


Lesenswert?

Hallo an alle!

Für die Datenzwischenspeicherung bei einer USB Kommunikation halte ich 
einen Ringpuffer sinnvoll. Ich hab damal ein bisschen etwqas 
geschrieben. Da ich noch nicht der große Prgrammier Chrack bin, hoffe 
ich, dass jemand über den Codde schaut und mir eventuell Kritikpunkte, 
bzw Verbesserungsborschläge gibt.

Derzeit habe ich 3 Funktionen implementiert. Die Initialisierung des 
Buffers, Ein Byte hinten anhängen und das vorderste Element auslesen. 
Passen die so??

Welche Funktionen sind denn noch sinnvoll??

Hier dier Code:
1
typedef ringbufferStruct
2
{
3
  unsigned char *ptr;
4
  u8 read;
5
  u8 write;
6
  u8 size;
7
} ringbuffer;
8
9
void ringpuffer_init(ringbuffer *buffer, unsigned char *adr, u8 size)
10
{
11
  buffer->ptr = adr;
12
  buffer->size = size;
13
14
  buffer->read = 0;
15
  buffer->write = 0;
16
}
17
18
void writeToEndOfBuffer(ringbuffer *buffer, unsigned char data)
19
{
20
  buffer->ptr[buffer->write] = data;    //Schreibe Daten an die aktuelle Position
21
22
  if((buffer->write++) >= buffer->size)  //Bei Bufferüberlauf Adresse wieder auf 0 setzen
23
    buffer->write = 0;
24
}
25
26
unsigned char getFromFrontOfBuffer(ringbuffer *buffer)
27
{
28
  unsigned char data = 0;
29
30
  if(buffer->read != buffer->write)  
31
  {
32
    data = buffer->ptr[buffer->read];
33
    buffer->read++;            //Leseadresse wird inkrementiert
34
35
    if(buffer->read >= buffer->size)  //Bei Bufferüberlauf Adresse wieder auf 0 setzen
36
      buffer->read = 0;
37
  }
38
39
  return data;  //0 Fehler, Sonst data
40
}

Ich hoffe es kann mir helfen

Danke im Voraus

Gruß Robert

von Karl H. (kbuchegg)


Lesenswert?

Dein Buffer kann überlaufen.
Wenn du genügend hineinschreibst ohne jemals aus dem
Buffer zu lesen, dann geht der Ringbuffer einmal
rundum und fängt irgendwann an, noch nicht abgeholtes
zu überschreiben.

von Robert (Gast)


Lesenswert?

Das stimmt. Da hast du recht. Jedoch wie kann ich das verhindern??

Wenn ich beim Schreiben if(buffer->write > buffer->read) abfrage, dan 
berücksichtige ich baber nicht wenn der Buffer einmal übergelaufen ist. 
zB read ist auf Element 70 und Write auf 10. Wie kann ich das 
berücksichtigen??

Danke im Voraus

Gruß Robert

von Karl heinz B. (kbucheg)


Lesenswert?

read:  70
write: 10

Wenn du das nächste Element geschrieben werden würde, wo
wird es geschrieben? Auf 10, write hat danach den Wert 11.
Das geht jetzt so weiter, bis write den Wert 69 erreicht
hat. Beim nächsten schreiben, wird also das nächste Element
auf die Position 69 geschrieben und write hat danach
den Wert 70. Beim nächsten Schreiben würde daher der
auf Position 70 geschrieben und dort steht aber ein Wert
der von read noch nicht geholt wurde.

Nimm Papier und Bleistift, mach einen Buffer der Größe 8
und spiel ein paar Beispiele durch. Du wirst sehen
dass du schnell heraus hast, wie das gehen muss. Das
Problem, das du jetzt nämlich hast ist, dass du den
Zustand Buffer-voll nicht vom Zustand Buffer-leer
unterscheiden kannst. In beiden Fällen sind die
beiden Index Variablen gleich.

Noch was:
Wenn du die Buffer Größe als eine 2-er Potenz auslegst,
dann kannst du das Erhöhen der Indizes einfacher gestalten:

   buffer->read = ( buffer->read + 1 ) & ( buffer->size - 1 );

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?


von Sven DerSchreckliche (Gast)


Lesenswert?

Wenn Du nicht einfach den Code abschreiben willst:
Einfach einen Zähler einbauen, der die Anzahl der im Buffer stehenden 
Daten zählt. Ist für C sicherlich die einfachste Methode.

Bei GetFromFrontOfBuffer würde ich im Fehlerfall(also wenn der Buffer 
leer ist) eine -1 zurückgeben, denn vielleicht hast Du ja auch 0(Null) 
als Datenbyte?! Und dann natürlich nicht als "char" sondern "short" 
definieren.

von Robert (Gast)


Lesenswert?

Ich hab das ganze jetzzt geändert. Zusätzlich in der Struktur habe ich 
noch eine Variable length die den Wert der aktuell gebrauchten 
Buffervariablen beinhaltet.

Kann das so funktioniern??
1
#include "ringpuffer.h"
2
3
typedef struct ringbufferStruct
4
{
5
  unsigned char *ptr;
6
  u8 read;
7
  u8 write;
8
  u8 size;
9
  u8 length;
10
} ringbuffer;
11
12
void ringbuffer_init(ringbuffer *buffer, unsigned char *adr, u8 size)
13
{
14
  buffer->ptr = adr;
15
  buffer->size = size;
16
17
  buffer->read = 0;
18
  buffer->write = 0;
19
  buffer->length = 0;
20
}
21
22
unsigned char writeToEndOfBuffer(ringbuffer *buffer, unsigned char data)
23
{
24
  if(buffer->length <= buffer->size)    //Wenn Buffer nict voll
25
  {
26
    buffer->ptr[buffer->write] = data;
27
    buffer->write++;
28
    buffer->length++;
29
30
    if(buffer->write >= buffer->size)
31
      buffer->write = 0;
32
33
    return 1;
34
  }
35
  
36
  return 2;
37
}
38
39
int16_t getFromFrontOfBuffer(ringbuffer *buffer)
40
{
41
   int16_t data = -1;
42
43
  if(buffer->length)    //Wenn Daten vorhanden sind
44
  {
45
    data = buffer->ptr[buffer->read];
46
    buffer->read++;
47
    buffer->length--;
48
49
    if(buffer->read >= buffer->size)
50
      buffer->read = 0;
51
  }
52
53
  return data;  //-1 Fehler, Sonst data
54
}

Danke im Voraus

Gruß Robert

von stinkywinky (Gast)


Lesenswert?

Ferner könnte es zu Problemen kommen, falls writeToEndOfBuffer() oder 
getFromFrontOfBuffer() von einem Interrupt gerufen werden, weil dann die 
Verwaltung aus dem Tritt kommt.

von Robert (Gast)


Lesenswert?

Das stimmt. Das lässt sich doch änderen wenn ich jede Variable mit 
volatile deklariere. Kann ich auch das struct als ganzes deklarieren??

zB volatile ringbuffer txBuffer??

Gruß Robert

von stinkywinky (Gast)


Lesenswert?

Da mit dem Volatile ist nur die halbe Miete. Sorge dafür, dass sich 
writeToEndOfBuffer() und getFromFrontOfBuffer() nicht gegenseitig 
unterbrechen können.

von Joe D. (kosmonaut_pirx)


Lesenswert?

hallo,
ich denke, man kann auch reentrant arbeiten. dazu muss sich gemerkt 
werden, wie viele calls schon in der jeweiligen routine sind. zugegeben, 
das "merken" muss atomar erfolgen.
in etwa stelle ich mir das so vor wie unten zu sehen, mal für ein lesen 
aus dem puffer. "Bounded_Simple_Queue" ist im prinzip ein ringbuffer. 
die syntax ist aus einem template, tut aber nichts zur sache, denke ich.

bool pop(T& __t){
    if ( ! Bounded_Simple_Queue::empty()){
      _rthreads++;
      __t = _elements[_front + _rthreads - 1];
      _rthreads--;
      _front = (_front + 1) % __size;
      _fill--;
      return false;
    }
    else{
      return true;
    }
  }

bye kosmo

von Robert (Gast)


Lesenswert?

Hallo

Wenn ich die Interrupts in den Funktionen deaktiviere, hab ich aber die 
Gefahr, dass ich Daten verliere.

Angenommen es kommt ein Interrupt in der Ohase in dem die Interrupts 
deaktivert sind. Der Interrupt geht verloren und die Daten werden nicht 
gesendet oder nicht ausgelesen. Was kann ich dagegen tun??

Gruß Robert

von Werner B. (Gast)


Lesenswert?

Solange du keine "level triggered" Interrupts verwendest, bleibt die 
Anforderung stehen. Level triggered können aber nur externe Interrupts 
sein. Wenn du die Interrupts nicht für Millisekunden sperrst geht auch 
nix verloren. Der MCU langweilt sich üblicherweise nur ;-)

von Robert (Gast)


Lesenswert?

Das Problem ist aber. Es sind externe Interrupts die ich benötige. Bei 
der USB Anbindung habe ich einen TX Interrupt wenn es gesendet wurde und 
einen RX Interrupt wenn etwas zum abholen da ist.

Wie kann ich das nun lösen?

von Werner B. (Gast)


Lesenswert?

Externe Interrupts sind zB INT0 bisn INT3 und Pin-Change Interrupts. 
Hier generiert das externe Signal DIREKT den Interrupt.
RX und TX sind interne Interrupts da sie von internen Quellen (USART, 
TIMER, ADC,...) erzeugt werden.

von Joe D. (kosmonaut_pirx)


Lesenswert?

wie geschrieben: merke dir, wieviele schon in die routinen gesprungen 
sind. du greifst quasi vor. ein '++' müsste auf dem avr atomar sein, 
cmiiw.

von Werner B. (Gast)


Lesenswert?

Nur ein ++ auf eine fixe Registervariable die entweder 8 Bit breit ist 
oder in X, Y oder Z vorgehalten wird. In ASM kann man das steuern, in C 
nur "bedingt". Fluch und Segen der RISC Architektur.

von Robert (Gast)


Lesenswert?

Wie ich oben geschrieben habe, benutze ich einen FT245 für eine USB 
ANbindung und nicht den Internen USART. Der IC hat 2 spezielle Pins. TX 
und RX. TX geht auf high wenn gesendet wurde und RX wenn etwas da ist. 
Die beiden hängen bei mir (Mega128) auf den externen Interrupts INT6 und 
INT7.

Auf den Senpuffer zum Beispiel greife ich außerhalb der ISR nur mit 
write zu und in der ISR nur mit read. Muss ich in dem Fall auch 
mitzählen??

Gruß Robert

von Robert S. (razer6)


Lesenswert?

Hab ich so das Problem auch??

von stinkywinky (Gast)


Lesenswert?

Überlege Dir folgendes Szenario:

Dein Programm ist gerade dabei zu senden, also irgendow in 
writeToEndOfBuffer(). Jetzt könnte zu jeder Zeit ein Empfangs-IRQ 
auftreten, sodass das Programm unterbrochen wird und 
getFromFrontOfBuffer() ausgeführt wird. In beiden Funktionen macht das 
Programm etwas mit buffer->read und puffer->length.

Bedenke ausserdem, dass eine Zeile C-Code meistens aus diversen 
ASM-Instruktionen besteht, also "mittendrin" unterbrochen werden kann.

Meiner Meinung nach musst Du das in jedem Fall geeignet absichern. Falls 
Du es nicht tust, wirst Du irgend wann mal hässliche Effekte bekommen, 
die eine langwierige Fehlersuche verursachen.

von lala (Gast)


Lesenswert?

hallo,
du brauchst nur die schreibzugriffe auf 'length' absichern

writeToEndOfBuffer:
...
<hier interrupts aus>
buffer->length++;
<hier interrupts wieder ein>
...

und in der lesefunktion ebenso.

die interrupts werden so schnell wieder eingeschaltet.
da sollte nichts verloren gehen.

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.