Pages: [1] 2   Go Down
Author Topic: Sämtliche Zeichen eines Arrays um eine Stelle verschieben  (Read 3022 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

da ich mir gerne einen FIFO-Buffer selber erstellen möchte, suche ich eine Möglichkeit um alle Zeichen eines Arrays (bis auf die "Ende-NULL" um eine Stelle nach links zu schieben. Das Zeichen an der ersten Stelle soll somit das Array "verlassen". Die Stelle vor der "Ende-NULL" würde ich im nächsten Programmierschritt durch ein "frisches" Zeichen füllen.

Wie macht man das am elegantesten?

Gruß Chris
« Last Edit: January 13, 2013, 02:33:17 am by Chris72622 » Logged


Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 271
Posts: 21881
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Am schnellsten ist daß Du nicht die Elemente des Array verschiebst sondern einfach den die Nullstelle des Index definierst und dort das nächste Zeichen hinschreibst. Nullindex+1 fängt das Array an und geht bis Nullindex. Wenn der Index = Größe des Array-1 ist dann get der Index bei 0 weiter.
Grüße Uwe
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

dann würde das Array doch aber so befüllt werden..

123
456
789

..und nicht so..

123
234
345

..was für mich ein Problem wäre, da ich z.B. dann ein Problem hätte, wenn ich nach einer "Textpassage" innerhalb des Arrays suchen wollen würde und sich diese "Textpassage" dann an Position 3 und 4 befinden würde.

Mein Plan ist es, seriell eingehende Zeichen kontinuierlich zu überprüfen.

Würde ich dabei einfach nur "im Kreis" in ein Array schreiben, könnte ich zusammenhängende Textpassagen u.U. nicht erkennen.

Den ersten Satz von Dir habe ich aber glaub grammatikalisch irgendwie nicht so ganz verstanden.

Gruß Chris
Logged


Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 271
Posts: 21881
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Alle Zähler beginen mit 0. Das Array hat zB 9 Elemente.

Im Array steht zB
123456789 und Anfangsposition 0  // Abfangssituation bei gefülltem Array
nächtes Zeichen A:
A23456789 und Anfangsposition 1
nächstes Zeichen B:
AB3456789 und Anfangsposition 2

Wenn Du einen Text in Array suchst zB "45"
dann findest Du ihn in
AB3456789, Anfangsposition 2 (was dem Array 3456789AB entspricht) an der Stelle 3 dann subtrahierst Du davon die Anfangsposition und erhälst die richtige Position 1.
Hoffe ich habe mich jetzt verständlicher ausgedrückt.

Die Alternative
123456789 ->23456789x -> 23456789A benötigt n Verschiebungen und das ist langsam.

Grüße Uwe
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich blick es nicht.

Vielleicht sollte ich das Ganze doch ehr mit einem Pointer aufbauen.

Trotzdem vielen Dank!

Gruß Chris
Logged


Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

so, nun bin ich (gedanklich) ein gutes Stückchen weiter.

Mit diesem Code bin ich bereits in der Lage ein Array immer wieder mit Zeichen zu befüllen:

Code:
char fifo[7] = "______";                                          // FIFO-Array (Platz für 6 Zeichen und eine Ende-Null)
char zeichen = 0;                                                 // Aktuelles Zeichen
int index = 0;                                                    // Indexzähler/Speicherstelle

void setup()
{
  Serial.begin(9600);
}

void loop()
{

while (Serial.available() > 0 && index < 7)
{
  zeichen=Serial.read();
  fifo[index]=zeichen;
  index++;
  if(index == 6)
  {
    index=0;
  }
  Serial.println(fifo);
}

}

Nun möchte ich den geschriebenen Inhalt sinnvoll weiterverarbeiten.

Die habe ich mir so vorgestellt:

Wenn z.B. auf Index "3" geschrieben wurde, sollen Zeiger (oder evtl. andere Dinge, welche ich aufgrund von Unkenntnis noch nicht kenne) auf folgende Indexes (heisst bestimmt anders) verweisen und ein "Zielarray" speisen:

4 5 0 1 2 3



Ziel soll ein Array sein, welches immer von rechts die "neuesten" Zeichen bekommt.

So in etwa in der Theorie:



Ich würde dann dieses Array von rechts auf bestimmt "Bedinungen" hin überprüfen (z.B. if(rechts im Array steht "OK\r) then "Aktion".

Mein Problem ist nun das Erstellen der Zeiger.

Hoffe, ich konnte meinen Ansatz genau genug beschildern.

Gruß Chris

PS: Naja- so hab ich es vorerst mal gelöst, hab aber das starke Gefühl, dass das nicht die optimale Lösung sein kann:

Code:
char fifo[7] = "______";                                          // FIFO-Array (Platz für 6 Zeichen und eine Ende-Null)
char zeichen = 0;                                                 // Aktuelles Zeichen
int index = 0;                                                    // Indexzähler/Speicherstelle

char ziel_array[7] = "______";

void setup()
{
  Serial.begin(9600);
}

void loop()
{

while (Serial.available() > 0 && index < 7)
{
  zeichen=Serial.read();
  fifo[index]=zeichen;
  index++;
  if(index == 1)
  {
    ziel_array[0]=fifo[1];
    ziel_array[1]=fifo[2];
    ziel_array[2]=fifo[3];
    ziel_array[3]=fifo[4];
    ziel_array[4]=fifo[5];
    ziel_array[5]=fifo[0];
  }
  if(index == 2)
  {
    ziel_array[0]=fifo[2];
    ziel_array[1]=fifo[3];
    ziel_array[2]=fifo[4];
    ziel_array[3]=fifo[5];
    ziel_array[4]=fifo[0];
    ziel_array[5]=fifo[1];
  }
  if(index == 3)
  {
    ziel_array[0]=fifo[3];
    ziel_array[1]=fifo[4];
    ziel_array[2]=fifo[5];
    ziel_array[3]=fifo[0];
    ziel_array[4]=fifo[1];
    ziel_array[5]=fifo[2];
  }
  if(index == 4)
  {
    ziel_array[0]=fifo[4];
    ziel_array[1]=fifo[5];
    ziel_array[2]=fifo[0];
    ziel_array[3]=fifo[1];
    ziel_array[4]=fifo[2];
    ziel_array[5]=fifo[3];
  }
  if(index == 5)
  {
    ziel_array[0]=fifo[5];
    ziel_array[1]=fifo[0];
    ziel_array[2]=fifo[1];
    ziel_array[3]=fifo[2];
    ziel_array[4]=fifo[3];
    ziel_array[5]=fifo[4];
  }
  if(index == 6)
  {
    ziel_array[0]=fifo[0];
    ziel_array[1]=fifo[1];
    ziel_array[2]=fifo[2];
    ziel_array[3]=fifo[3];
    ziel_array[4]=fifo[4];
    ziel_array[5]=fifo[5];
  }
  if(index == 6)
  {
    index=0;
  }
  Serial.print(fifo);
  Serial.print("   ");
  Serial.print(ziel_array);
  Serial.print("   Index:");
  Serial.print(index);
  Serial.println();
}

}
« Last Edit: January 14, 2013, 06:09:59 am by Chris72622 » Logged


Germany S-H
Offline Offline
Faraday Member
**
Karma: 172
Posts: 3252
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PS: Naja- so hab ich es vorerst mal gelöst, hab aber das starke Gefühl, dass das nicht die optimale Lösung sein kann:

Besonders dann nicht, wenn der Zeichenpuffer vielleicht auch mal 10, 50, 100 oder 200 Zeichen groß sein soll.
;-)

Also mit einem FIFO-Ringpuffer funktioniert es so:
Du hast den Puffer selbst, einen Lesezeiger und einen Schreibzeiger

Der Lesezeiger und der Schreibzeiger können dabei Pointer (Größe eines Pointers auf Arduino: 2 Bytes) auf Speicherstellen im Array sein. Oder wenn der Ringpuffer nur maximal 255 Zeichen enthält, können es einfach Indexangaben (1 Byte) für das Array sein. Um möglichst speichersparend zu programmieren also einen Leseindex und einen Schreibindex als "byte".

Zu Anfang sind Leseindex und Schreibindex gleich null, sie verweisen auf array[0], und immer wenn Leseindex und Schreibindex gleich sind, bedeutet das so viel: Puffer ist leer, es kann nichts gelesen werden.

Zum Füllen des Puffers hast du ja schon herausgefunden wie es geht: Du füllst das nächste Zeichen an der aktuellen Indexstelle ein (Dein "index" ist also der Schreibindex) und schaltest den Schreibindex um eins weiter. Am Ende des Puffers springt der Schreibindex wieder auf null.

Für eine ganz billige Lösung brauchst Du jetzt nur noch einen Leseindex, der genau so implementiert ist. Wenn Leseindex==Schreibindex ist der Puffer leer, dann nichts lesen. Ansonsten nach dem Lesen eines Zeichens immer den Leseindex um eins weitersetzen, am Ende wieder auf 0, und gelesen werden kann, bis Leseindex==Schreibindex erfüllt ist.

Knifflig wird es mit so einem Puffer nur, wenn er überlaufen kann, d.h. wenn mehr Zeichen in den Puffer geschrieben werden als es seiner Größe entspricht, bevor er ausgelesen wird.

Die einfachste und wenig sinnvolle Möglichkeit ist: Beim Overflow wird der Schreibindex weitergeschaltet bis auf den Leseindex. Dann ist der Puffer logisch "leer", kann nicht mehr ausgelesen werden, die im Puffer enthaltenen Zeichen sind verloren.

Am zweiteinfachsten zu implementieren und deutlich sinnvoller wäre folgende Overflow-Logik: Wenn ein Zeichen den Puffer vollgeschrieben hat und das Versetzen des Schreibzeigers zu der Bedingung Leseindex==Schreibindex führen würde, dann den Schreibindex NICHT versetzen. So bleibt dann die geschriebene Zeichenzahl im Puffer zwar erhalten (und kann ausgelesen werden), aber der nächste Schreibvorgang führt dazu, dass das zuletzt geschriebene Zeichen wieder überschrieben wird (d.h. es geht ein Zeichen verloren, jedesmal wenn ein Zeichen in einen vollen Puffer geschrieben werden soll).

Falls Du Fragen zur Realisierung hast, kann ich ggf. auch ein paar Codeschnipsel zu einem FIFO-Ringpuffer raussuchen.
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh Gott, bitte ja!!

Die Suche im Interent macht mich echt noch fertig grad.

Gruß Chris
Logged


Germany S-H
Offline Offline
Faraday Member
**
Karma: 172
Posts: 3252
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh Gott, bitte ja!!

Anbei mal ein paar Deklarationen und Funktionen zur Verwaltung eines FIFO-Ringpuffers.

Code:
#define FIFOSIZE 25
char fifoBuf[FIFOSIZE]; // ring buffer
byte fifoReadIndex,fifoWriteIndex;  // read and write index into ring buffer

void fifoWrite(char c)
// write char into buffer
{
  fifoBuf[fifoWriteIndex]=c; // store the char
  fifoWriteIndex++;  // advance write pointer in ringbuffer
  if (fifoWriteIndex>=FIFOSIZE) fifoWriteIndex=0; // ring buffer is at its end


char fifoRead()
// read char from buffer
// always check first if char is available with fifoAvailable()
// before reading the buffer using this function
{
  char c;
  c=fifoBuf[fifoReadIndex];
  fifoReadIndex++;
  if (fifoReadIndex>=FIFOSIZE) fifoReadIndex=0;
  return(c);


boolean fifoAvailable()
// checks for available chars in buffer
// char is available for reading if (fifoReadIndex!=fifoWriteIndex)
{
  return (fifoReadIndex!=fifoWriteIndex);


Falls FIFOSIZE über 255 groß werden soll, müssen fifoReadIndex,fifoWriteIndex als int statt byte deklariert werden.

Falls man andere Datentypen als char puffern möchte, ist dies durch kleinere Änderungen an fifoBuf, fifoWrite() und fifoRead() möglich.

Falls der FIFO-Puffer in Interrupt-Serviceroutinen verwendet werden soll, so dass er z.B. in einer ISR mit Daten gefüllt und in der loop ausgelesen werden soll, sind ebenfalls kleinere Änderungen notwendig.

Und immer dran denken: Die Funktion fifoRead() darf erst dann aufgerufen werden, nachdem mit fifoAvailable() geprüft wurde, ob tatsächlich etwas zum Auslesen im Puffer vorhanden ist!

Also zum Auslesen immer starten mit:
while (fifoAvailable()) ...
oder
if (fifoAvailable()) ...

Vielleicht kannst Du etwas damit anfangen.
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1000 Dank!!

Werd mich durchwühlen.. smiley

Gruß Chris
Logged


Germany S-H
Offline Offline
Faraday Member
**
Karma: 172
Posts: 3252
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1000 Dank!!

Werd mich durchwühlen.. smiley

Dann mal zu! Zwei Anmerkungen zu meinem Ringpuffer-Code noch:

1. Maximal speicherbare Zeichenzahl
Die maximal speicherbare Zeichenzahl im Puffer ist "FIFOSIZE minus eins".
Grund: Da der Zustand (fifoReadIndex==fifoWriteIndex) als "Ringspeicher ist leer" definiert ist, müssen sich bei einem Daten enthaltenden Ringpuffer der Readindex und Writeindex unterscheiden, daher kann der Puffer prinzipbedingt nicht bis aufs letzte Zeichen gefüllt werden, sondern ein Zeichen muß mindestens als Lücke zwischen ReadIndex und WriteIndex unbelegt bleiben. D.h. ein 25 Zeichen großer Puffer kann 25 verschiedene Zustände annehmen (leer sein oder 1-24 Zeichen enthalten), aber er kann keine 25 auslesbaren Zeichen enthalten.

2. In dem geposteten Code habe ich nur die von mir weiter oben als am wenigsten sinnvolle Methode im Falle eines Pufferüberlaufs implementiert: Bei einem Puffer-Overrun überrennt der WriteIndex den ReadIndex und damit wird der Pufferspeicher leer und der gesamte Inhalt ist quasi gelöscht und kann nicht mehr ausgelesen werden.

Hier noch die bei Pufferüberläufen etwas sinnvollere Version für die Speicherfunktion zum Austauschen gegen die oben gepostete:
Code:
void fifoWrite(char c)
// write char into buffer
{
  fifoBuf[fifoWriteIndex]=c; // store the char
  c=fifoWriteIndex;  // store old WriteIndex
  fifoWriteIndex++;  // advance write pointer in ringbuffer
  if (fifoWriteIndex>=FIFOSIZE) fifoWriteIndex=0; // ring buffer is at its end
  if (fifoReadIndex==fifoWriteIndex) fifoWriteIndex=c; // restore old WriteIndex
}

Mit dieser Schreibfunktion für den Puffer geht beim Puffer-Overrun immer nur das letzten Zeichen im Puffer durch Überschreiben verloren, während der Anfang des Puffers erhalten und zum einwandfreien Auslesen verfügbar bleibt.
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Vielen Dank für den Nachtrag.

Konnte beide Codes zusammenführen und bekomme nun "sichere" Werte aus der Funktion geliefert.

Muss ich die "Auswertung" des Arrays innerhalb einer Funktion vornehmen, oder kann man auch eine Funktion erstellen, die auf einmal gleich mehrere Zeichen übergibt; sprich eine array-Rückmeldungs-Funktion.

Leider habe ich ja noch immer das Problem, dass ich nicht nur einzelne Zeichen auswerten möchte, sondern (wie in dem unteren meiner beiden hochgeladenen Bilder zu sehen) eine Zeichenkette.

Ich könnte mir auch eine Überprüfung auf \r vorstellen. Hierfür müsste ich ja aber dann wiederum auf die zuvor in den Ringbuffer geschriebenen Zeichen zugreifen können.

Leider kann ich werder im Netz noch in meinen Büchern was dazu finden.

Bin über jede Hilfe dankbar!

Gruß Chris
Logged


Germany S-H
Offline Offline
Faraday Member
**
Karma: 172
Posts: 3252
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Leider habe ich ja noch immer das Problem, dass ich nicht nur einzelne Zeichen auswerten möchte, sondern (wie in dem unteren meiner beiden hochgeladenen Bilder zu sehen) eine Zeichenkette.

Ich könnte mir auch eine Überprüfung auf \r vorstellen. Hierfür müsste ich ja aber dann wiederum auf die zuvor in den Ringbuffer geschriebenen Zeichen zugreifen können.

So ein Ringspeicher-FIFO-Puffer ist eine reine Datenspeicherstruktur: Daten reinstopfen und Daten rausholen.

Die Verarbeitung müssen dann andere Routinen Deines Programms erledigen. Eine möglicherweise sinnvolle Erweiterung für den Ringpuffer, wenn Du bis zum Auftreten eines bestimmten Zeichens \r auslesen möchtest, jedoch ohne dieses Zeichen auszulesen, wäre eine "peek" Funktion für eine kurze Vorausschau des nächsten Zeichens im Puffer (oder ob das Zeichen überhaupt im Puffer verfügbar ist), jedoch OHNE das Zeichen dabei auszulesen. Das kann manchmal ganz praktisch sein.

Solche Peek-Funktion läßt sich ohne großen Aufwand realisieren.

Für das Auswerten von Daten im Puffer müßtest Du dann eine neue Funktion schreiben, die die Daten aus dem Puffer rauszieht und weiter verarbeitet. Was da am sinnvollsten ist, hängt ab, was da erkannt und weiter verarbeitet werden soll. Ob man z.B. regelrecht einen "Parser" schreiben muß, der Daten aufwändig analysiert. Oder ob man einfach nur einzelne Worte aus dem Ringpuffer rauszieht in ein Char-Array, auf das man dann z.B. Stringvergleiche und anderes anwenden kann.

Leider habe ich ja noch immer das Problem, dass ich nicht nur einzelne Zeichen auswerten möchte, sondern (wie in dem unteren meiner beiden hochgeladenen Bilder zu sehen) eine Zeichenkette.

Und die Zeichenkette kommt von der seriellen Schnittstelle?

Dass ich hier in den letzten Wochen schon mehrfach "Kommando-Erkennungsparser für die serielle Kommunikation" gepostet habe, hast Du aber schon gesehen und konntest damit nichts anfangen?

Z.B. von mir in http://arduino.cc/forum/index.php/topic,136027.0.html
Reply #3 on: December 08, 2012, 04:33:25 PM »

Oder von mir zu einer noch spezielleren Auswertung in http://arduino.cc/forum/index.php/topic,137669.0.html
Reply #4 on: December 16, 2012, 07:58:37 PM

Da kommen die Daten über seriell rein und bestimmte "Kommandos" kann das Programm dann erkennen und darauf reagieren.
Logged

Offline Offline
God Member
*****
Karma: 11
Posts: 599
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Und die Zeichenkette kommt von der seriellen Schnittstelle?

Dass ich hier in den letzten Wochen schon mehrfach "Kommando-Erkennungsparser für die serielle Kommunikation" gepostet habe, hast Du aber schon gesehen und konntest damit nichts anfangen?

Ja, serieller Eingang.

Mich verwirrt, dass viele "Befehle" in einer Programmiersprache geschrieben sind, die ich nicht in Einklang bringen kann mit dem, was ich in den Büchern über reine Arduinoprogrammierung bisher gelernt habe (z.B. "memset" oder "strstr").

Das sich aus meiner Sicht bei Beiden von Dir verlinkten Codeschnipseln der Buffer innerhalb der setup loop() befindet, kann ich das Ganze nun leider nicht in Einklang mit bisher von Dir "vorgeschlagenen" Funktionsvariante bringen.

Da ich ohne delay() arbeiten möchte, da ich noch anderen Code abzuarbeiten habe, wäre mir die "Funktionsvariante" viel lieber, doch mit dem Umbauen haperts ganz gewaltig. smiley-sad

Was "peek" ist, bin ich gerade am ergooglen. smiley

Ah- wenn ich es mit den "arduinoeigenen Befehlen" lösen wollenwürde, müsste ich vermutlich das hier nutzen, oder:

http://arduino.cc/en/Serial/Peek

In meinem Fall würde es aureichen auf Zeichenfolgen zu prüfen (wie "CONNECT" bei dem Kollegen, bloß inkl. Leerzeichen- z.B. "Z 3 0 1").

Vielleicht kann mich ja das hier glücklich machen:

http://my.safaribooksonline.com/book/hobbies/9781449399368/serial-communications/receiving_multiple_text_fields_in_a_s

Das deutsche Buch liegt vor mir; von daher sollte der Knoten sich so langsam mal lösen.. smiley

Gruß Chris
« Last Edit: January 15, 2013, 04:51:33 am by Chris72622 » Logged


Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 271
Posts: 21881
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Die ArduinoIDE benutzt den offiziellen AVR GCC Compiler von Atmel.  Dieser hat den gesamten C bzw Großteil von C++ Befehlsatz / Bibiotheken und all diese Funktionen kannst Du verwenden.
Arduino erklärt nicht den gesamten Befehlsatz/ Funktionen.
Grüße Uwe
Logged

Pages: [1] 2   Go Up
Jump to: