mikrocontroller.net

Forum: PC-Programmierung uC UART - libserial unter Linux in C++


Autor: Dieter J. (dieter_josef)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich bin dabei eine einfache Kommunikation zwischen einem Arduino 
(Adafruit WICED) und einem Beaglebone mit einem Ubuntu aufzubauen. Es 
soll eine mehr oder weniger Realtime fähige Regelung entstehen. Der 
Arduino soll dabei lediglich einige Sensorwerte einlesen (nimmt etwas 60 
us Laufzeit in Anspruch), diese verrechnen und sie als eine 
Integerzahl/Chararray über einen UART an den Beaglebone senden. Mit dem 
BBB soll ausschließlich gelesen werden. Also nur 2 Leitungen. SIGNAL/GND

Der Testcode für den Arduino besteht aus dem Senden einer Zahl von 
1-32000, die sich jede Millisekunde erhöhrt. D.h. nach etwa 32 sec 
beginnt der uC von vorne die Daten zu übertragen. Dient mit der 
Überprüfung ob ich wirklich die aktuellen Daten des uCs bekomme.
int test = 1;

void setup()
{
  Serial.begin(115200);
  while (!Serial)
  {
    delay(1);
  }
}

void loop()
  Serial.println(test);
  test++;
  if(test >= 32000){
    test = 1;
  }
  delayMicroseconds(1000);
}


Auf dem Beaglebone Black möchte ich diese nun Einlesen. Wollte dieses 
mit der libserial machen. Ich wollte dieses eigentlich direkt über 
termios machen und habe mich angefangen dort einzulesen. Bin aber auf 
dem Weg an libserial hängengeblieben.
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <SerialStream.h>

using namespace std;
using namespace LibSerial;

const int LOOP_UPDATE = 1;

int main()
{
  double time_counter = 0;
  clock_t this_time = clock();
  clock_t last_time = this_time;


  const int buffersize = 8;
  char input_buffer[buffersize];
  string tmpBuffer = "";

  LibSerial::SerialStream serial_uart_1;
  serial_uart_1.SetBaudRate(SerialStreamBuf::BAUD_115200);
  serial_uart_1.SetCharSize(SerialStreamBuf::CHAR_SIZE_8);
  serial_uart_1.SetParity(SerialStreamBuf::PARITY_NONE);
  serial_uart_1.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
  serial_uart_1.SetNumOfStopBits(1);

  serial_uart_1.Open("/dev/ttyACM0"); //Test auf dem Computer über USB

  while (1)
  {
    this_time = clock();
    time_counter += (double)(this_time - last_time);
    last_time = this_time;
    
    /*Auslesen des UARTS mit 10 Samples per sec*/
    if(time_counter > (double)(LOOP_UPDATE * CLOCKS_PER_SEC/10))
    {
      time_counter -= (double)(LOOP_UPDATE * CLOCKS_PER_SEC/10);
      serial_uart_1.read(input_buffer, buffersize);
      tmpBuffer = string(input_buffer);
      cout << tmpBuffer;
    }
  }

  serial_uart_1.Close();
  return(0);
}

Ich habe mit UART noch nicht gearbeitet und denke, dass ich noch einige 
grundlegende Verständnisprobleme damit habe. Ich habe mir das 
folgendermaßen vorgestellt:

Ich sende alle 200 us die Daten (z.B. als Hex bei max (int)32000 wäre 
man mit 2 Bytes fertig). Auf dem Beaglebone wollte ich dann in der Loop 
etwa im 1 ms Zyklus die Daten einfach direkt dem Stream entnehmen. Der 
UART Port fängt an zu empfangen wartet auf den Startbit und überträgt 
die 2 Bytes und wäre damit fertig. So war meine Vorstellung.

Bei meinem Code lese ich jedoch nur den Buffer aus d.h. in jeder Loop 
auf dem Beaglebone spring er nur einen digit hoch. Heißt für mich, dass 
der uC den Buffer befüllt und der BBB sich mit dem "veralteten" Daten 
beschäftigt. "Der uC läuft dem BBB einfach weg". Bei 0.1 s Looptime im 
Test müsste ich jedoch Wertänderungen in 100ern sehen. Dazu kommen noch 
die gelegentlichen Lücken.

Bedeutet für mich, dass der uC schon seine Arbeit richtig verrichtet ich 
auf der BBB Seite jedoch den Buffer flute und mich mit dem Abarbeiten 
dieser Daten beschäftige, obwohl schon lange neue Daten vom uC 
bereitliegen müssten. Ich weiß nicht genau wo ich genau anfangen soll 
den Fehler zu suchen.

Könnte mir jemand diesbezüglich Tipps geben, wo meine gedanklichen 
Fehler liegen?

Viele Grüße

Dieter

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter J. schrieb:
> Ich sende alle 200 us die Daten (z.B. als Hex bei max (int)32000 wäre
> man mit 2 Bytes fertig)

Das machst Du nicht, Du sendest als ASCII-String mit abschließendem 
Zeilenumbruch. Bei 115200 Baud dauert das Senden eines Zeichens etwa 100 
µs, die längste Zeichenkette ist fünf Ziffern + Zeilenumbruch lang, 
dauert also etwa 600 µs.

Es ist bei einer seriellen Übertragung durchaus sinnvoll, einen 
"Protokollrahmen" zu verwenden, der dem Empfänger die Synchronisation 
auf die gesendeten Daten ermöglicht; würdest Du Deinen Wert in binärer 
Form (das ist das, was Du fälschlicherweise als "Hex" bezeichnest) 
übertragen, gäbe es diesen Protokollrahmen nicht, und der Empfänger wäre 
nicht in der Lage, herauszufinden, welche Bytes zusammengehören.

Insofern ist Deine Übertragung als ASCII-String schon mal gar nicht so 
schlecht, sie ist nur aufgrund der gewählten Dezimaldarstellung und der 
resultierenden unterschiedlich langen Telegramme etwas ungeschickt.

Du könntest Deine Zahl aber auch als vierstellige Hexzahl (mit führenden 
Nullen) senden, wiederum gefolgt von einem Zeilenumbruch, das wären also 
konstante fünf Zeichen und also eine Dauer von 500 µs pro Telegramm.

Autor: Dieter J. (dieter_josef)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Danke für die schnelle Antwort.

Habe jetzt meinen Arduino Code angepasst. Jetzt gehen nur noch 
Telegramme mit genau 6 Zeichen raus. (4 Zeichen Daten, 2 Zeichen Ascii 
13).

Mir bleibt noch die essentielle Frage, wie das mit dem Buffer aussieht, 
den ich oben mit 8 Zeichen initialisiert habe. Mit meinem C Code werden 
weiterhin einfach die Buffer in jeder Schleife ein digit weiter 
ausgegeben. Am Programmstart nimmt er den aktuellen Wert und zählt 
einfach nur um einen hoch, obwohl er nur 10 mal pro sec aufgerufen wird. 
Ich verstehe nicht nicht.

Ich habe jedoch die Vermutung, dass irgendein Buffer direkt voll 
beschrieben wird und anschließend von der read Methode jeweils Absatz 
für Absatz ausgelesen wird (wird dann im Programm in den Char array 
geschrieben). Das würde zumindest das Verhalten erklären.

Gibt es dazu eventuell eine Lektüre die mir helfen könnte das besser zu 
verstehen?

VG
Dieter

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter J. schrieb:
> wie das mit dem Buffer aussieht, den ich oben mit 8 Zeichen
> initialisiert habe.


Dieter J. schrieb:
> char input_buffer[buffersize];

Den Buffer hast Du nicht initialisiert. Du hast ihn nur angelegt, der 
Inhalt aber ist, da es sich um eine automatische Variable handelt, 
undefiniert.

Ich weiß nicht, was Deine read()-Funktion treibt, daher kann ich Deine 
weitere Frage nicht beantworten.

Hast Du die geschrieben? Gibt es davon Quelltext oder Dokumentation?

Beitrag #5258529 wurde von einem Moderator gelöscht.
Autor: Dieter J. (dieter_josef)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Das mit dem Initialisieren habe ich verwechselt.


ja die Quelle dazu ist hier:
https://github.com/crayzeewulf/libserial

dort sind die Klassen hinterlegt.

Auch wenn ich deren Beispiel nehme und nur eine Schleife drüberlege, 
erhalte ich ebenfalls das Verhalten. Er ließt nur den Buffer aus, aber 
nicht den aktuellen sondern macht einfach nur da weiter wo er aufgehört 
hat.

Ich habe die Vorlage "read_port.cpp" genommen und nur eine while 
Schleife gelegt.
#include <SerialStream.h>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <stdio.h>

using namespace LibSerial;


int main()
{
    // Instantiate the SerialStream object then open the serial port.
    SerialStream serial_stream;
    serial_stream.Open("/dev/ttyACM0");

    if ( !serial_stream.good() )
    {
        std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] "
                  << "Error: Could not open serial port."
                  << std::endl ;
        exit(1) ;
    }

    // Set the baud rate of the serial port.
    serial_stream.SetBaudRate(SerialStreamBuf::BAUD_115200);

    if ( !serial_stream.good() )
    {
        std::cerr << "Error: Could not set the baud rate." << std::endl ;
        exit(1) ;
    }

    // Set the number of data bits.
    serial_stream.SetCharSize( SerialStreamBuf::CHAR_SIZE_8 ) ;

    if ( !serial_stream.good() )
    {
        std::cerr << "Error: Could not set the character size." << std::endl ;
        exit(1) ;
    }

    // Disable parity.
    serial_stream.SetParity( SerialStreamBuf::PARITY_NONE ) ;

    if ( !serial_stream.good() )
    {
        std::cerr << "Error: Could not disable the parity." << std::endl ;
        exit(1) ;
    }

    // Set the number of stop bits.
    serial_stream.SetNumOfStopBits(1);

    if (!serial_stream.good())
    {
        std::cerr << "Error: Could not set the number of stop bits."
                  << std::endl ;
        exit(1) ;
    }

    // Turn off hardware flow control.
    serial_stream.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
    if ( !serial_stream.good() )
    {
        std::cerr << "Error: Could not use hardware flow control."
                  << std::endl ;
        exit(1) ;
    }

    // Do not skip whitespace characters while reading from the serial port.
    // serialStream.unsetf(std::ios_base::skipws);

    // Wait for some data to be available at the serial port.
    while( serial_stream.rdbuf()->in_avail() == 0 )
    {
        usleep(100) ;
    }

    // Keep reading data from serial port and print it to the screen.
    while(1)
    {
      std::cout << "--new loop--" << std::endl;
      while( serial_stream.rdbuf()->in_avail() > 0 )
      {
        char nextByte;
        serial_stream.get(nextByte);
        std::cout << nextByte;
        usleep(1000);
      }
      usleep(100000);
    }

    std::cerr << std::endl;
    return EXIT_SUCCESS ;
}


Ausgabe:
--new loop--
233C
233D
233E
233F
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
234A
234B
234C
234D
234E
234F
2350
2351
2352
2353
2354
2355
2356
2357
--new loop--
2358
2359
...

Was ich eigentlich von dem Programme erwarte ist das hier:
--new loop--
233C
--new loop--
2358
Kann es an dem Buffertyp liegen? Ich möchte lediglich nur den aktuellen 
Wert(oder zumindest den letzten geschrieben vollständigen Wert) des uC 
abholen.

Danke für die Hilfe :)

VG
Dieter

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.