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:
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.
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
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 );
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.
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??
Ferner könnte es zu Problemen kommen, falls writeToEndOfBuffer() oder
getFromFrontOfBuffer() von einem Interrupt gerufen werden, weil dann die
Verwaltung aus dem Tritt kommt.
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
Da mit dem Volatile ist nur die halbe Miete. Sorge dafür, dass sich
writeToEndOfBuffer() und getFromFrontOfBuffer() nicht gegenseitig
unterbrechen können.
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
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
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 ;-)
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?
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.
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.
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
Ü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.
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.