Arduino Micro - Wie Buffer leeren?

Hallo zusammen, ich bin Einsteigerin hier :)

Ich habe ein Problem- Kurz gesagt werden Daten von einem Labview Programm an den Arduino Micro gesendet und dort für weitere Zwecke benutzt. Die Daten des Labview Programms werden über ein USB-RS232 Kabel an den Arduino über UART übermittelt.

Die Verbindung ist hergestellt und funktioniert, allerdings gibt es eine sehr große Verzögerung beim verarbeiten der ankommenden Daten im Arduino. Da ich mein Program halbwegs echtzeitfähig laufen lassen wollen, möchte ich diese Verzögerung möglichst klein halten. Es ist für mein Programm nicht nötig alle Daten zu verwenden, also will ich einen Großteil der Daten aus dem Buffer Löschen bevor ich sie wieder erneut von Labview einlese.

Könnte eine Abwandlung der Flush() Funktion in der HardwareSerial Klasse hierbei Abhilfe schaffen? Gibt es noch andere Möglichkeiten den Buffer des Arduinos zu leeren?

Viele Grüße

Hallo Maren,
welche Art von Daten muss denn übermittelt werden? Strings? Zahlen?
Für was brauchst du eine solche Verbindung?
Eine etwas bessere Beschreibung wäre gut, wir können ja schließlich keine Gedanken lesen :wink:
LG The Programmer

Hallo,

Glaskugel on.

Mit flush wird nur sichergestellt, dass der Hardware UART Sendebuffer komplett geleert wird bevor es weitergeht. Also das Daten komplett gesendet sind.

Ich denke du redest von deinem eigenen Einlesebuffer was ein char array oder so ist. Das kann man leeren in dem man an Indexpos. 0 eine Null Terminierung setzt oder alles mit Nullen beschreibt.

memset(message,'\0',sizeof(message));  // Lesebuffer löschen

Hallo The_Programmer,

Danke für deine schnelle Rückmeldung. Es werden Strings übermittelt, diesen sehen beispielsweise so aus: AABBCCDD. Diese Strings werden wieder umgerechnet in ihre Hexadezimalwerte also aus AA wird 170... Die Zahlen werden dann verwendet, um eine weitere Komponente anzusteuern. Es ist vorgegeben, dass die Daten aus Labview kommen müssen. Ich hoffe das sind die Infos, die du wolltest. Hast du eine Idee?

Hallo Doc_Arduino,

mir ist klar für was die Flush Funktion jetzt da ist. Meine Frage bezog sich auf ein Post in einem anderen Forum, indem beschrieben worden ist, dass die Flushfunktion die Funktion hatte, den Einlesebuffer zu leeren/löschen dabei wurde ein Beispiel mitgeliefert:

void HardwareSerial::flush() { _rx_buffer->head = _rx_buffer->tail; }

Beim Versuch dieses Beispiel zu verwenden bekam ich allerdings eine Zugriffverweigerung, was allerdings daran liegen könnte, dass ich mein Arduino Programm in dem ProgramFiles Ordner von Windows installiert habe, bevor ich allerdings das Programm deinstalliere und wo anders wieder erneut installiere, wollte ich zunächst klären, ob das so funktionieren könnte.

Ich würde zuerst versuchen, die gesendeten Daten minimal zu halten. Also wenn du jeweils 2 Hexadezimalzahlen ausliest, aber nur 30 verschiedene Zahlen (von 255) brauchst, kannst du die anderen Bits weglassen (2^5 = 32, 3 Bits fallen weg). Hast du das Programm selbst geschrieben oder ist es ein Vorgegebenes?

Hallo,

flush löscht nichts. Es wartet bis der [u]Sende[/u]puffer der UART komplett entleert wurde. Aber nicht durch löschen wie du das meinst sondern in dem alles gesendet wurde.

Was willste denn nun wirklich löschen?

Hinter der Funktion steht auch etwas anderes was man dir erzählt hat. Dabei werden im UART Register Flags abgefragt.

void HardwareSerial::flush()
{
  // If we have never written a byte, no need to flush. This special
  // case is needed since there is no way to force the TXC (transmit
  // complete) bit to 1 during initialization
  if (!_written)
    return;

  while (bit_is_set(*_ucsrb, UDRIE0) || bit_is_clear(*_ucsra, TXC0)) {
    if (bit_is_clear(SREG, SREG_I) && bit_is_set(*_ucsrb, UDRIE0))
    // Interrupts are globally disabled, but the DR empty
    // interrupt should be enabled, so poll the DR empty flag to
    // prevent deadlock
    if (bit_is_set(*_ucsra, UDRE0))
      _tx_udr_empty_irq();
  }
  // If we get here, nothing is queued anymore (DRIE is disabled) and
  // the hardware finished tranmission (TXC is set).
}

Edit: man keine Bits einfach so weglassen. Es wird immer Byteweise gesendet und empfangen. Erst muß das Byte komplett vorliegen bevor das manipulieren kann. Was hier sinnlos wäre. Ist ja schon komplett eingelesen.

Hallo Programmer, Ich verstehe nicht genau was du mir sagen willst. Der String der von meinem LabView-Programm gesendet wird, ist immer 8 Bytes lang, aus diesen 8 Bytes werden dann 4 Hexadezimal-Zahlen, die alle benötigt werden.

Das LabView Programm ist ein vorgegebenes, das noch von mir erweitert wurde.

Hallo Doc_Arduino,

ja ich habe mir die flush Funktion auch angeschaut. Ich weis, dass sie überhaupt nicht das macht was ich möchte. Ich benötige jedoch die flush Funktion mit ihrer eigentlich Funktion nicht, deshalb dachte ich ich verwende sie um eine neue Funktion zu schreiben. Also könnte ich quasi auch jede andere Funktion aus dieser Datei nehmen die ich nicht benötige oder auch eine neue schreiben, mit der ich Zugriff auf auf gewisse Variablen habe, um sie zu verändern(die die wichtig für den RX-Buffer sind). Es kann sein dass das keinen Sinn macht, ich dachte nur, es wäre ein Ansatz.

Ich würde gerne immer bevor ich einen neuen String auslese, die Daten die in der Zwischenzeit angekommen sind löschen, so dass ich den aktuellsten String einlese.

Für das Auslesen verwende ich übrigens folgende Funktion:

Serial1.readBytesUntil()

Hallo,

genau dafür hatte ich dir den Befehl schon gezeigt mit memset.
Ersetze “message” durch den Namen deines Einlesebuffers.

Angenommen man möchte die Zahlen 9, 26, 15 und 49 senden. Mit deinem Verfahren bräuchte man 4 mal 8 Bits, also 32 Bits. Wie bereits von mir erwähnt brauchen diese Zahlen anstatt 8 Bits nur 6 Bits, das heißt, dass man die 6 Bits der Zahl 49 (110001) per Bitmanipulation auf die drei anderen Zahlen aufteilen kann. Somit sendest du anstatt diesen 4 Bytes 00 001001 (9) 00 011010 (26) 00 001111 (15) 00 110001 (49) nur diese 3 Bytes 11 001001 (2 Bits von 48, alle 6 Bits von 9) 00 011010 (2 weitere Bits von 48, alle 6 Bits von 26) 01 001111 (letzten 2 Bits von 48, alle 6 Bits von 15) Dadurch sparst du dir das Senden eines Bytes.

Ich hoffe ich habe das einigermaßen verständlich beschrieben :)

Hallo Doc-Arduino,

Ah ok, entschuldige, das hatte ich falsch verstanden. Und ich bin mir immer noch nicht sicher ob ich das habe. Ich schreibe also den Befehl von dir vor meiner Einlesefunktion? Mein eigener Lesebuffer ist ein byte array, welches acuh nur 8 Bytes groß ist.

Hallo Programmer,

achso ja klar, vielen Dank für die genaue Beschreibung, jetzt hab ichs verstanden. Ich glaube jedoch dass es bei meinem Problem nicht hilft. Mein Labview Programm sendet mehrere Strings pro Sekunde und mein Arduino kann nur 1 oder 2 pro Sekunde auslesen, weil die Nachverarbeitung der Daten so lange geht. Alle weiteren Daten sammeln sich dann an und meine Echtzeitfähigkeit ist dahin. Mein Problem wäre theoretisch gelöst wenn mein Labview Programm nur 1 oder 2 Strings pro Sekunde senden würde. Da ich einen großen Teil des Labview Programms jedoch nicht selbst geschrieben habe und meine Kenntnisse in LabView nicht gut sind, trau ich mir nicht zu in de rZeit die ich jetzt noch habe dies in meinem LabView Programm zu verwirklichen.

Hallo,

das mit den Bytes verschachteln ist zwar eine geniale Idee. Zugegeben. Nur Aufwand und Nutzen ... ich weis nicht recht. Aber ansonsten genial.

Die Funktion mit memset macht genau das was du dir wünscht. Es überschreibt den Inhalt jeder Indexposistion mit Null "\0". Teste es einfach aus. Erstelle dir ein array mit irgendwelchen Inhalt, lasse es dir anzeigen, danach memset drüber und wieder Inhalt anzeigen lassen. Und wenn du statt Null eine 8 übergibts, steht im array überall eine 8 drin.

Vielleicht zeigst du mal dein Programm,das kommt mir aber arg langsam vor, evtl blockierst du ein einer Stelle unbeabsichtigt den Ablauf.

Hallo Doc_Arduino,

ich würde aber gerne meinen "Buffer" löschen bevor ich die Daten einlese, weil das ja dann zur Verzögerung führt. Und sei mir nicht böse wenn ich dich wieder falsch verstanden habe, aber dien Befehl würde ja nur das löschen ermöglichen, nachdem ich meine Daten eingelesen habe. Genau das Einlesen möchte ich vermeiden, aber dadurch dass die Daten dann langsamer eingelesen werden als mein Ardu sie ausliest heist ja, dass sie irgendwo zwischengespeichert werden, also in einem Buffer, den meine Read Funktion dann ausliest und in mein Array schreibt. Diesen würde ich gerne löschen.

Hallo,

was hindert dich daran deinen array Buffer vorher zu löschen? Also direkt vorm einlesen.
Wann du den einsetzt ist ganz dir überlassen. Auch wenn du den nach dem einlesen und verarbeiten löschst,
ist er damit ja vorm erneuten einlesen gelöscht. Mußte ja nicht doppelt löschen.
Oder reden wir jetzt aneinander komplett vorbei?

Verzögerung gibts da auch keine beim löschen. Das geht so schnell. Dein Programm verdrödelt sowieso irgendwo viel Zeit. Hast du delays drin?

Hallo Programmer,
mein Programm ist sehr groß und enthält vielle verschiedenen klassen und Funktionen, aber hier mal der Ausschnitt den ich für wichtig halte:

if(Serial1.available()>0) 
{
  
    Serial1.readBytesUntil('L',ReceivedData,8) ;
   
    for(unsigned short int z=0;z<=6;z=z+2)
    {
      
            if(ReceivedData[z] < 'A'){
               high= ReceivedData[z] - '0';
            }
            else
           {
              high = ReceivedData[z] + 10 - 'A';
           }
            if(ReceivedData[z+1] < 'A'){
              low = ReceivedData[z+1] - '0';
            }
            else
            {
          low = ReceivedData[z+1] + 10 - 'A';
           
        }

       MsgData[u]= (high << 4) + low; 
       Serial1.println(MsgData[u]);
      
       u++;
 
    }
   ...

Das L in der read Funktion ist einfach ein character der niemals ankommt, weil ich die 8 Bytes als Abbruchkriterium will.
danach wird jede Zahl einzeln abgefragt und verwendet.

Hallo Doc_Arduino,

mein Problem ist dass ich nicht weiß welchen Buffer ich löschen soll. Also wie heist der Buffer in dem die ankommenden Daten gespeichert werden bevor ich sie einlese? Ich kenne ja nur das Array in das ich die Daten einlese.

Sorry für die komischen Striche am Ende meines Codes, weiß nicht was da passiert ist.

Maren89:
Hallo Doc_Arduino,

mein Problem ist dass ich nicht weiß welchen Buffer ich löschen soll. Also wie heist der Buffer in dem die ankommenden Daten gespeichert werden bevor ich sie einlese? Ich kenne ja nur das Array in das ich die Daten einlese.

Sorry für die komischen Striche am Ende meines Codes, weiß nicht was da passiert ist.

genau darum geht es doch. Um dein Array was du als Einlesebuffer verwendest. Nachdem das verarbeitet ist, will du dessen Inhalt löschen. Den Buffer der Hardware UART brauchste nicht löschen. Der ist leer nachdem du das in dein Array ausgelesen hast. Natürlich mußte das komplett auslesen.

Moment, jetzt verstehe ich dein Problem. Du liest nur 8 Bytes ein. Es werden jedoch mehr als 8 gesendet und der Rest steht noch im Buffer der UART. Dann lies komplett ein.
Oder mit

while(Serial.read() >= 0);  // Empfangsbuffer leeren

da wird bis der Null Terminator kommt quasi ins Nirwana gelesen. while blockiert aber auch.
Am Ende ist es egal ob du in deiner Einlesefunktion komplett einliest oder nur ein paar Bytes und dann den Rest verwirfst. Zeitlich komplett egal. Wenn du Übertragungszeit sparen möchtest, darf dein Labview nur soviel Daten senden wie du benötigst und nicht deutlich mehr.

An Hand vom Codeschnipsel können wir nicht erkennen wo es im Ablauf klemmen könnte. Häng am besten die .ino ran.