Forum: Compiler & IDEs UART Baudrate runterrechnen


von Jürgen P. (juergenp)


Lesenswert?

Moin,
Ich habe einen ATmega1280 auf dem Arduino Mega Board.
Hier will ich einen UART mit 38400 Baud auslesen, die empfangenen Werte 
bearbeiten, und an einem anderen UART mit 9600 Baud wieder rausgeben.
Wenn ich mit gleicher Baudrate sende, ist alles kein Problem. Das 
Einlesen hatte ich in eine Extra-Funktion gesetzt.
Die Funktion liest von UART1 mit 38,4kBaud 20 Zeichen, und wandelt die 
gewünschten Teile davon in einen Double Wert. Diesen gibt die Funtion 
zurück.
Das Hauptprogramm selber liest dann den Rückgabewert, und verarbeitet 
ihn noch weiter, um ihn anschließend als char-Array über UART0 mit 9600 
Baud auszugeben.
Ich dachte nun, dass die Funktion ja eigentlich bei Aufruf nur einen 
Wert ausliest, und sofort an das Hauptprogramm liefert, welches es dann 
weiterevrarbeitet. Dann erst beim nächsten Aufruf wieder einen Wert 
einliest, und so weiter.... Müsste es dann nicht egal sein, dass die 
Baudrate unterschiedlich ist? Programmintern müsste doch eigentlich der 
selbe Double Wert aus der Funktion kommen....
Hätte sonst keine Vorstellung, wie man die Baudrate runter bekommt. Hab 
leider auch nichts dazu im Netz gefunden.

von Michael U. (amiga)


Lesenswert?

Hallo,

das wird wohl zuerst davon abhängen, daß die effektive Baudrate der 
38400 nicht höher ist als 9600 Baud, also passend wenig Daten 
(Empfangsbuffergröße), lange Pausen (umsortieren und mit 1/4 der 
Geschwindikeit senden).

Sonst geht es schon prinzipiell nicht.

Gruß aus Berlin
Michael

von Jürgen P. (juergenp)


Lesenswert?

Was soll DAS bedeuten?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Na, ganz einfach: Mit 38400 Baud können knapp 4000 Zeichen pro Sekunde 
übertragen werden. Mit 9600 aber nur knapp 1000.

Stell Dir einfach mal eine vierspurige Autobahn vor, die plötzlich auf 
eine Spur verengt wird ... Damit es keinen brutalen Stau gibt, dürfen 
auf der vierspurigen Autobahn nicht mehr Autos unterwegs sein, als auf 
der einspurigen Strecke vorankommen können.

von Jürgen P. (juergenp)


Lesenswert?

Ja, das grundlegende Problem ist mir schon klar.
Ich hänge mal den Code ran.
Hier ist jetzt die Baudrate von UART0 noch auf 38400. Wenn ich im 
Hauptprogramm ein delay einsetzte, beispielsweise 100ms, bekomme ich 
auch bei allen paar Zeilen nur Mist.
1
char Data[20];
2
char Data1[20];
3
int FOGint;
4
char FOGout[20];
5
char FOGbuf[20];
6
7
void setup() {
8
 Serial.begin(38400);
9
 Serial1.begin(38400);
10
}
11
12
// Auslesen der UART1 mit 38400 Baud und Konvertierung des empfangenen Arrays
13
// in Double Wert
14
15
double FOG(void) {
16
   int inByte;
17
   int i;
18
   double FOGreturn;
19
20
   // warten bis CR empfangen
21
   inByte = 0;
22
   do {
23
     if (Serial1.available()) inByte = Serial1.read();  
24
   } while (inByte != 13);
25
   i = 0;
26
   do {
27
     // Wenn Daten verfügbar 20 Zeichen in Data schreiben
28
     if (Serial1.available()) {
29
       Data[i] = Serial1.read();
30
     i++;
31
     }
32
   } while (i<20);
33
   // Aus den 20 Zeichen die relevanten rausziehen
34
   for (int i=5; i<=11; i++){
35
      Data1[i-5] = Data[i];
36
   }
37
   // Abschließende Null für gültigen String
38
   Data1[i-5] = 0;    
39
   
40
   FOGreturn = atof(Data1);
41
   return FOGreturn;
42
43
} 
44
void loop(){
45
  
46
  FOGint = FOG() * 100;
47
//  Serial.print(FOGint);
48
//  Serial.print("\r\n");
49
  sprintf(FOGbuf, "%i", FOGint);
50
  int vk = strlen(FOGbuf) - 2;
51
  int gesl = strlen (FOGbuf);
52
53
  //Minus und '0.' vor den Wert setzten
54
    if (FOGint < 0 && gesl <=3){
55
      FOGout[0] = '-';
56
      FOGout[1] = '0';
57
      FOGout[2] = '.';
58
      if (gesl == 3){
59
       FOGout[3] = FOGbuf[1];
60
       FOGout[4] = FOGbuf[2];
61
        FOGout[5] = 0;
62
       }
63
       else if (gesl == 2){
64
        FOGout[3] = '0';
65
        FOGout[4] = FOGbuf[1];
66
        FOGout[5] = 0;
67
       }
68
      }
69
  // Nur '0.' vor den Wert setzten
70
      else if (FOGint >= 0 && gesl <=2){
71
        FOGout[0] = '0';
72
        FOGout[1] = '.';
73
        if (gesl == 2){
74
          
75
          FOGout[2] = FOGbuf[0];
76
          FOGout[3] = FOGbuf[1];
77
          FOGout[4] = 0;
78
          }
79
          else if (gesl == 1){
80
            FOGout[2] = '0';
81
            FOGout[3] = FOGbuf[0];
82
            FOGout[4] = 0;
83
        }
84
       }
85
   // Minus vor Wert mit Komma einsetzten
86
        else if (FOGint < 0 && gesl >3){
87
          FOGout[0] = '-';
88
          for (int i=1; i<= (gesl-3); i++){
89
            FOGout[i] = FOGbuf[i];
90
            }
91
          FOGout[gesl-2] = '.';
92
          FOGout[gesl-1] = FOGbuf[gesl-2];
93
          FOGout[gesl] = FOGbuf[gesl-1];
94
          FOGout[gesl+1] = 0;
95
          }
96
    // Nur Komma einsetzen
97
          else if (FOGint >=0 && gesl >2){
98
            for (int i=0; i<= (gesl-2); i++){
99
              FOGout[i] = FOGbuf[i];
100
              }
101
            FOGout[gesl-2] = '.';
102
            FOGout[gesl-1] = FOGbuf[gesl-2];
103
            FOGout[gesl] = FOGbuf[gesl-1];
104
            FOGout[gesl+1] = 0;
105
            }
106
            else FOGout[0] = 'E';
107
108
      Serial.print("$TIROT,");      
109
      Serial.print(FOGout);
110
      Serial.print(",A");
111
      Serial.print("\r\n");
112
        
113
      }

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Was um alles in der Welt machst Du da?

Wozu ist das denn gut?

Beschreibe bitte mal das mit 38400 empfangene Protokoll und das 
Protkoll, das Du daraus mit 9600 erzeugen willst.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Rufus t. Firefly schrieb:

> Wozu ist das denn gut?

Das geht vielleicht in diese Richtung:
http://www.cruzpro.com/rp110.html

Aus einem Gerät (Drehgeber mit UART Ausgang) ein anderes Gerät (RATE OF 
TURN DEGREES/MINUTE) machen... also eine Art NMEA Data repeater oder ein 
NMEA Konverter.

Die Idee halte ich für machbar, wenn wie vermutet beide UARTs nicht "in 
der Sättigung betrieben werden".

Um weiter zu kommen würde ich wie von Rufus vorgeschlagen den 
Eingangsstring und den Ausgangsstring exemplarisch zeigen und die 
Berechnung dazwischen.

für eine erste Version würde ich auf die Berechnung sogar verzichten und 
z.B. den Empfangsstring 1:1 weitergeben. Wenn das problemlos 
funktioniert, dann kann die Auswertung und Umformung dazugeschrieben 
werden.

von Karl H. (kbuchegg)


Lesenswert?

Vor allen Dingen muss man eines auch einmal klipp und klar sagen:
So wird das nichts.

Dein Programm darf nicht aktiv auf eine Eingabe warten.
Das Lesen vom Sender muss per Interrupt passieren.
Im Interrupt werden die Zeichen gesammelt, bis eine Zeile beisammen ist. 
Liegt die Zeile vor, wird sie aus dem Empfangsbuffer sofort umkopiert, 
damit der Empfangsbuffer für die nächsten Zeichen frei wird. Dieses 
Umkopieren in einen Ausgangsbuffer darf aber nur dann passieren, wenn 
der Ausgangspuffer als verfügbar markiert ist. Gelingt das Umkopieren, 
wird der Ausgangspuffer sofort als nicht verfügbar markiert, damit der 
nächste Hereinkommende String den Ausgangspuffer nicht überschreibt 
solange er bearbeitet wird. Erst nachdem der String nicht mehr benötigt 
wird, wird der Ausgangspuffer wieder als verfügbar markiert.

Ist eine Zeile im Ausgangspuffer holt sich die Hauptschleife den String, 
bearbeitet ihn, zb indem sie sich den double Wert rausholt, setzt den 
Ausgangspuffer wieder auf verfügbar und macht mit dem Wert  was immer 
sie möchte und schickt das Ergebnis auf der anderen UART wieder raus.

So wäre es vernünftig, so dass möglichst wenig Datensätze verloren gehen

von Karl H. (kbuchegg)


Lesenswert?

@Alle

könnt ihr mal kontrollieren, ob der ganze if-else Pallawatsch in loop 
identisch ist zu
1
void loop()
2
{
3
  FOGint = FOG() * 100;
4
5
  Serial.print("$TIROT,");      
6
7
  if( FOGint >= 0 )
8
    sprintf( FOGout, "0.%03d", FOGint );
9
  else
10
    sprintf( FOGout, "-0.%03d", -FOGint );
11
12
  Serial.print(FOGout);
13
14
  Serial.print(",A\r\n");
15
}

von Random .. (thorstendb) Benutzerseite


Lesenswert?

**LOOL** das ist geil!

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

>     sprintf( "0.%03d", FOGint );

Bestimmt nicht ;-)

von Karl H. (kbuchegg)


Lesenswert?

Stefan B. schrieb:
> Karl heinz Buchegger schrieb:
>
>>     sprintf( "0.%03d", FOGint );
>
> Bestimmt nicht ;-)


Grade gesehen und korrigiert.
Aber die restliche Logik müsste doch genau das machen? Zumindest was ich 
so im Überfliegen des Originalcodes gesehen habe. (*)

Ich würds für mich sogar noch kürzen
1
void loop()
2
{
3
  int FOGint = FOG() * 100;
4
5
  if( FOGint >= 0 )
6
    sprintf( FOGout, "$TIROT,0.%03d,A\r\n", FOGint );
7
  else
8
    sprintf( FOGout, "$TIROT,-0.%03d,A\r\n", -FOGint );
9
10
  Serial.print( FOGout );
11
}

(*) Das Ganze wäre so viel einfacher, wenn er nicht so eine bescheuerte 
Einrückungsstrategie hätte.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:

> könnt ihr mal kontrollieren, ob der ganze if-else Pallawatsch in loop
> identisch ist zu

Korrektur. Er geht auf Hunderstel, nicht auf Tausendstel. Die vielen 
3-er im Originalcode haben mich verwirrt.
Ausserdem gibt es unten noch irgendeinen Spezialfall, den ich noch nicht 
richtig entschlüsselt habe.

1
void loop()
2
{
3
  FOGint = FOG() * 100;
4
5
  Serial.print("$TIROT,");
6
7
  if( FOGint >= 0 )
8
    sprintf( FOGout, "0.%02d", FOGint );
9
  else
10
    sprintf( FOGout, "-0.%02d", -FOGint );
11
12
  Serial.print(FOGout);
13
 
14
  Serial.print(",A\r\n");
15
}

von Karl H. (kbuchegg)


Lesenswert?

Also:

Ich habs falsch gelesen.
Der Zweck des Codes ist es einen double mit exakt 2 Nachkommastellen 
auszugeben.

Da im Code soewieso schon die Floating Point Lib benutzt wird (für 
atof), dürfte eine Verwendung der float-sprintf Version auch schon egal 
sein. Die meiste Zeit geht sowieso auf die UART Übertragung drauf.

Der Code ist daher äquivalent zu
1
void loop( void )
2
{
3
  sprintf( FOGout, "$TIROT,%.2f,A\r\n", FOG() );
4
  Serial.print( FOGout );
5
}

Wem sprintf zu heavy für den Fall ist, kann auch auf dtostrf ausweichen.

Auf jeden Fall hat diese Version hier auch noch den Vorteil, dass 
korrekt gerundet wird und bei grossen Zahlen kein Überlauf durch den 
temporären int stattfinden wird ( FOG() > 320 )

von Karl H. (kbuchegg)


Lesenswert?

Zur Empfangsroutine:

Wie funktioniert die Serielle Schnittstellenklasse?
Was meldet available()? (Annahme: Das ein Zeichen eingetroffen ist)
Wartet die read Funktion auf ein Zeichen oder kehrt die auch dann zurück 
wenn nichts verfügbar ist?

Auf jeden Fall würde ich mir da erst mal eine Hilfsfunktion machen
1
char ReceiveChar()
2
{
3
  //
4
  // Warten bis ein Zeichen da ist
5
  //
6
  while( !Serial1.available() )
7
    ;
8
9
  //
10
  // Zeichen liefern
11
  //
12
  return Serial1.read();
13
}
14
15
double FOG(void)
16
{
17
  uint8_t i;
18
19
  //
20
  // auf den nächsten Datensatz synchronisieren
21
  // der vorhergehende ist genau dann zu Ende, wenn ein
22
  // \n empfangen wird. Danach beginnt der nächste Datensatz
23
  //
24
  while( ReceiveChar() != '\n' )
25
    ;
26
27
  //
28
  // Ein Datensatz sind genau 20 Zeichen
29
  //
30
  for( i = 0; i < 20; ++i )
31
    Data[i] = ReceiveChar();
32
33
  //
34
  // daraus interessiert ein double Wert, der an Position 5 beginnt
35
  // und aus 7 Zeichen besteht.
36
  //
37
  Data[5+7] = '\0';
38
  return atof( &Data[5] );
39
}

von Jürgen P. (juergenp)


Lesenswert?

Also,
Ich hätte mir die ganze Mühe unter loop auch gerne geschenkt, dort die 
Zahl wieder so zusammen zu wurschteln. Allerdings habe ich mir sagen 
lassen, dass der ATmega1280 keine double Werte in Strings umwandeln 
kann. Natürlich hatte ich das zuvor auch schon so programmiert, und war 
schon ziemlich am Ende, weil es nicht ging. Also musste ich mir was 
basteln.
Wenn Ihr einen Weg kennt, ihm das beizubringen, sagt mir bitte wie. So, 
wie es zuletzt kbuchegg gepostet hat gehts nicht.
Den Rest muss ich mir gleich erst nochmal durchlesen.
Danke schon mal für die vielen Antworten...

von Karl H. (kbuchegg)


Lesenswert?

Jürgen Papadopolis schrieb:

> lassen, dass der ATmega1280 keine double Werte in Strings umwandeln
> kann.

Lass mich raten.
In deiner Ausgabe tauchen immer ? auf

http://www.mikrocontroller.net/articles/FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio

> Allerdings habe ich mir sagen
> lassen, dass der ATmega1280 keine double Werte in Strings umwandeln
> kann.

Wer sagt den sowas?
Es ist nicht gerade das Schnellste, ja. Aber das ist Floating Point 
Verarbeitung sowieso auf einem Mega nicht, weil alles in Software 
gemacht werden muss. Egal welcher Mega.

von Karl H. (kbuchegg)


Lesenswert?

> Also musste ich mir was basteln.

Du hättest dir zb auch das hier basteln können
1
void loop()
2
{
3
  FOGint = FOG() * 100;
4
5
  Serial.print("$TIROT,");      
6
7
  sprintf( FOGout, "%d.%02d", FOGint / 100, FOGint  % 100 );
8
  Serial.print(FOGout);
9
10
  Serial.print(",A\r\n");
11
}

Moral von der Story: Kenne dein Handwerkszeug!

von Jürgen P. (juergenp)


Lesenswert?

Naja, mir wurde nicht gesagt, dass er es gar nicht kann, sondern dass 
der Rechenaufwand zu groß wäre.
Genau, es kommt ein ? Ich benutze für die Programmierung einen Compiler 
der zum Arduino-Board gehört. Das schien erstmal unkomplizierter zu 
sein, weil man direkt über USB verbinden kann. Kein Ahnung, ob oder wie 
man dort die floating-point Unterstützung aktiviert.
Aber so wie ichs mache, gehts ja auch. Also lacht nicht! ;-)

von Karl H. (kbuchegg)


Lesenswert?

Jürgen Papadopolis schrieb:
> Naja, mir wurde nicht gesagt, dass er es gar nicht kann, sondern dass
> der Rechenaufwand zu groß wäre.

Mag sein.
Auf der anderen Seite ist deine Applikation mit Sicherheit nicht 
Calculation-bound sondern I/O-bound.
Soll heißen: Der wesentliche zeitbestimmende Faktor in deiner App ist 
die Übertragung per UART. Du optimierst an der falschen Stelle.

> Genau, es kommt ein ? Ich benutze für die Programmierung einen Compiler
> der zum Arduino-Board gehört.

Welchen?

(Wenn da ein ? kommt, ist die Chance ziemlich gross, dass es im Grunde 
auch nur ein gcc ist)

> Das schien erstmal unkomplizierter zu
> sein, weil man direkt über USB verbinden kann. Kein Ahnung, ob oder wie
> man dort die floating-point Unterstützung aktiviert.
> Aber so wie ichs mache, gehts ja auch. Also lacht nicht! ;-)

Nimm wenigstens die kürzere Variante, die das mittels / und % 
auseinanderpfriemelt.

von Jürgen P. (juergenp)


Lesenswert?

Ich würde das, wie gesagt, gerne so machen, aber ich weiß noch nicht, 
wie man das dem Compiler sagt, das er mit Float bei sprintf klarkommt. 
Er basiert tatsächlich auf gcc, hat aber nicht so umfangreiche 
Einstellungsmöglichkeiten. Zumindest bringt mich die Anleitung aus dem 
Forum nicht weiter.
Die Ausführung oben, mit der Extrafunktion ReceiveChar von kbuchegg 
würde im Grunde ja auch nichts ändern, oder? So wie ich es jetzt gemacht 
habe, umständlich oder nicht, funktioniert es ja, solange die Baudraten 
identisch sind.
Zu den Interrupts: Ehrlich gesagt (dem ein oder anderen ists 
wahrscheinlich schon aufgefallen) bin ich nicht unbedingt ein C - 
Muttersprachler. Daher ist es für mich auch (bisher) relativ komfortabel 
mit dem Arduino Compiler zu arbeiten. Der nimmt einem doch einiges an 
Arbeit ab hab ich das Gefühl.
Wenn es noch ein Beispiel für die Interrupts gäbe, könnte ich mir das 
nochmal ansehen. Das hab ich noch nicht geblickt.

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.