www.mikrocontroller.net

Forum: Compiler & IDEs Frage zu Ringpuffer


Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
typedef ringbufferStruct
{
  unsigned char *ptr;
  u8 read;
  u8 write;
  u8 size;
} ringbuffer;

void ringpuffer_init(ringbuffer *buffer, unsigned char *adr, u8 size)
{
  buffer->ptr = adr;
  buffer->size = size;

  buffer->read = 0;
  buffer->write = 0;
}

void writeToEndOfBuffer(ringbuffer *buffer, unsigned char data)
{
  buffer->ptr[buffer->write] = data;    //Schreibe Daten an die aktuelle Position

  if((buffer->write++) >= buffer->size)  //Bei Bufferüberlauf Adresse wieder auf 0 setzen
    buffer->write = 0;
}

unsigned char getFromFrontOfBuffer(ringbuffer *buffer)
{
  unsigned char data = 0;

  if(buffer->read != buffer->write)  
  {
    data = buffer->ptr[buffer->read];
    buffer->read++;            //Leseadresse wird inkrementiert

    if(buffer->read >= buffer->size)  //Bei Bufferüberlauf Adresse wieder auf 0 setzen
      buffer->read = 0;
  }

  return data;  //0 Fehler, Sonst data
}

Ich hoffe es kann mir helfen

Danke im Voraus

Gruß Robert

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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 );

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das gibts übrigens auch schon fertig:
http://koders.com/c/fidE0865314AEBADF6A9DA08E989C4...

Autor: Sven DerSchreckliche (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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??
#include "ringpuffer.h"

typedef struct ringbufferStruct
{
  unsigned char *ptr;
  u8 read;
  u8 write;
  u8 size;
  u8 length;
} ringbuffer;

void ringbuffer_init(ringbuffer *buffer, unsigned char *adr, u8 size)
{
  buffer->ptr = adr;
  buffer->size = size;

  buffer->read = 0;
  buffer->write = 0;
  buffer->length = 0;
}

unsigned char writeToEndOfBuffer(ringbuffer *buffer, unsigned char data)
{
  if(buffer->length <= buffer->size)    //Wenn Buffer nict voll
  {
    buffer->ptr[buffer->write] = data;
    buffer->write++;
    buffer->length++;

    if(buffer->write >= buffer->size)
      buffer->write = 0;

    return 1;
  }
  
  return 2;
}

int16_t getFromFrontOfBuffer(ringbuffer *buffer)
{
   int16_t data = -1;

  if(buffer->length)    //Wenn Daten vorhanden sind
  {
    data = buffer->ptr[buffer->read];
    buffer->read++;
    buffer->length--;

    if(buffer->read >= buffer->size)
      buffer->read = 0;
  }

  return data;  //-1 Fehler, Sonst data
}

Danke im Voraus

Gruß Robert

Autor: stinkywinky (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: stinkywinky (Gast)
Datum:

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

Autor: Joe Die (kosmonaut_pirx)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Joe Die (kosmonaut_pirx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Robert Schilling (razer6)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab ich so das Problem auch??

Autor: stinkywinky (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: lala (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.