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


von Dieter J. (dieter_josef)


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.
1
int test = 1;
2
3
void setup()
4
{
5
  Serial.begin(115200);
6
  while (!Serial)
7
  {
8
    delay(1);
9
  }
10
}
11
12
void loop()
13
  Serial.println(test);
14
  test++;
15
  if(test >= 32000){
16
    test = 1;
17
  }
18
  delayMicroseconds(1000);
19
}


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.
1
#include <stdio.h>
2
#include <time.h>
3
#include <iostream>
4
#include <SerialStream.h>
5
6
using namespace std;
7
using namespace LibSerial;
8
9
const int LOOP_UPDATE = 1;
10
11
int main()
12
{
13
  double time_counter = 0;
14
  clock_t this_time = clock();
15
  clock_t last_time = this_time;
16
17
18
  const int buffersize = 8;
19
  char input_buffer[buffersize];
20
  string tmpBuffer = "";
21
22
  LibSerial::SerialStream serial_uart_1;
23
  serial_uart_1.SetBaudRate(SerialStreamBuf::BAUD_115200);
24
  serial_uart_1.SetCharSize(SerialStreamBuf::CHAR_SIZE_8);
25
  serial_uart_1.SetParity(SerialStreamBuf::PARITY_NONE);
26
  serial_uart_1.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
27
  serial_uart_1.SetNumOfStopBits(1);
28
29
  serial_uart_1.Open("/dev/ttyACM0"); //Test auf dem Computer über USB
30
31
  while (1)
32
  {
33
    this_time = clock();
34
    time_counter += (double)(this_time - last_time);
35
    last_time = this_time;
36
    
37
    /*Auslesen des UARTS mit 10 Samples per sec*/
38
    if(time_counter > (double)(LOOP_UPDATE * CLOCKS_PER_SEC/10))
39
    {
40
      time_counter -= (double)(LOOP_UPDATE * CLOCKS_PER_SEC/10);
41
      serial_uart_1.read(input_buffer, buffersize);
42
      tmpBuffer = string(input_buffer);
43
      cout << tmpBuffer;
44
    }
45
  }
46
47
  serial_uart_1.Close();
48
  return(0);
49
}

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

von Rufus Τ. F. (rufus) Benutzerseite


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.

von Dieter J. (dieter_josef)


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

von Rufus Τ. F. (rufus) Benutzerseite


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.
von Dieter J. (dieter_josef)


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.
1
#include <SerialStream.h>
2
#include <iostream>
3
#include <unistd.h>
4
#include <cstdlib>
5
#include <stdio.h>
6
7
using namespace LibSerial;
8
9
10
int main()
11
{
12
    // Instantiate the SerialStream object then open the serial port.
13
    SerialStream serial_stream;
14
    serial_stream.Open("/dev/ttyACM0");
15
16
    if ( !serial_stream.good() )
17
    {
18
        std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] "
19
                  << "Error: Could not open serial port."
20
                  << std::endl ;
21
        exit(1) ;
22
    }
23
24
    // Set the baud rate of the serial port.
25
    serial_stream.SetBaudRate(SerialStreamBuf::BAUD_115200);
26
27
    if ( !serial_stream.good() )
28
    {
29
        std::cerr << "Error: Could not set the baud rate." << std::endl ;
30
        exit(1) ;
31
    }
32
33
    // Set the number of data bits.
34
    serial_stream.SetCharSize( SerialStreamBuf::CHAR_SIZE_8 ) ;
35
36
    if ( !serial_stream.good() )
37
    {
38
        std::cerr << "Error: Could not set the character size." << std::endl ;
39
        exit(1) ;
40
    }
41
42
    // Disable parity.
43
    serial_stream.SetParity( SerialStreamBuf::PARITY_NONE ) ;
44
45
    if ( !serial_stream.good() )
46
    {
47
        std::cerr << "Error: Could not disable the parity." << std::endl ;
48
        exit(1) ;
49
    }
50
51
    // Set the number of stop bits.
52
    serial_stream.SetNumOfStopBits(1);
53
54
    if (!serial_stream.good())
55
    {
56
        std::cerr << "Error: Could not set the number of stop bits."
57
                  << std::endl ;
58
        exit(1) ;
59
    }
60
61
    // Turn off hardware flow control.
62
    serial_stream.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
63
    if ( !serial_stream.good() )
64
    {
65
        std::cerr << "Error: Could not use hardware flow control."
66
                  << std::endl ;
67
        exit(1) ;
68
    }
69
70
    // Do not skip whitespace characters while reading from the serial port.
71
    // serialStream.unsetf(std::ios_base::skipws);
72
73
    // Wait for some data to be available at the serial port.
74
    while( serial_stream.rdbuf()->in_avail() == 0 )
75
    {
76
        usleep(100) ;
77
    }
78
79
    // Keep reading data from serial port and print it to the screen.
80
    while(1)
81
    {
82
      std::cout << "--new loop--" << std::endl;
83
      while( serial_stream.rdbuf()->in_avail() > 0 )
84
      {
85
        char nextByte;
86
        serial_stream.get(nextByte);
87
        std::cout << nextByte;
88
        usleep(1000);
89
      }
90
      usleep(100000);
91
    }
92
93
    std::cerr << std::endl;
94
    return EXIT_SUCCESS ;
95
}

Ausgabe:
1
--new loop--
2
233C
3
233D
4
233E
5
233F
6
2340
7
2341
8
2342
9
2343
10
2344
11
2345
12
2346
13
2347
14
2348
15
2349
16
234A
17
234B
18
234C
19
234D
20
234E
21
234F
22
2350
23
2351
24
2352
25
2353
26
2354
27
2355
28
2356
29
2357
30
--new loop--
31
2358
32
2359
33
...

Was ich eigentlich von dem Programme erwarte ist das hier:
1
--new loop--
2
233C
3
--new loop--
4
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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.