mikrocontroller.net

Forum: Compiler & IDEs UART Baudrate runterrechnen


Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was soll DAS bedeuten?

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

Bewertung
0 lesenswert
nicht 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.

Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht 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.
char Data[20];
char Data1[20];
int FOGint;
char FOGout[20];
char FOGbuf[20];

void setup() {
 Serial.begin(38400);
 Serial1.begin(38400);
}

// Auslesen der UART1 mit 38400 Baud und Konvertierung des empfangenen Arrays
// in Double Wert

double FOG(void) {
   int inByte;
   int i;
   double FOGreturn;

   // warten bis CR empfangen
   inByte = 0;
   do {
     if (Serial1.available()) inByte = Serial1.read();  
   } while (inByte != 13);
   i = 0;
   do {
     // Wenn Daten verfügbar 20 Zeichen in Data schreiben
     if (Serial1.available()) {
       Data[i] = Serial1.read();
     i++;
     }
   } while (i<20);
   // Aus den 20 Zeichen die relevanten rausziehen
   for (int i=5; i<=11; i++){
      Data1[i-5] = Data[i];
   }
   // Abschließende Null für gültigen String
   Data1[i-5] = 0;    
   
   FOGreturn = atof(Data1);
   return FOGreturn;

} 
void loop(){
  
  FOGint = FOG() * 100;
//  Serial.print(FOGint);
//  Serial.print("\r\n");
  sprintf(FOGbuf, "%i", FOGint);
  int vk = strlen(FOGbuf) - 2;
  int gesl = strlen (FOGbuf);

  //Minus und '0.' vor den Wert setzten
    if (FOGint < 0 && gesl <=3){
      FOGout[0] = '-';
      FOGout[1] = '0';
      FOGout[2] = '.';
      if (gesl == 3){
       FOGout[3] = FOGbuf[1];
       FOGout[4] = FOGbuf[2];
        FOGout[5] = 0;
       }
       else if (gesl == 2){
        FOGout[3] = '0';
        FOGout[4] = FOGbuf[1];
        FOGout[5] = 0;
       }
      }
  // Nur '0.' vor den Wert setzten
      else if (FOGint >= 0 && gesl <=2){
        FOGout[0] = '0';
        FOGout[1] = '.';
        if (gesl == 2){
          
          FOGout[2] = FOGbuf[0];
          FOGout[3] = FOGbuf[1];
          FOGout[4] = 0;
          }
          else if (gesl == 1){
            FOGout[2] = '0';
            FOGout[3] = FOGbuf[0];
            FOGout[4] = 0;
        }
       }
   // Minus vor Wert mit Komma einsetzten
        else if (FOGint < 0 && gesl >3){
          FOGout[0] = '-';
          for (int i=1; i<= (gesl-3); i++){
            FOGout[i] = FOGbuf[i];
            }
          FOGout[gesl-2] = '.';
          FOGout[gesl-1] = FOGbuf[gesl-2];
          FOGout[gesl] = FOGbuf[gesl-1];
          FOGout[gesl+1] = 0;
          }
    // Nur Komma einsetzen
          else if (FOGint >=0 && gesl >2){
            for (int i=0; i<= (gesl-2); i++){
              FOGout[i] = FOGbuf[i];
              }
            FOGout[gesl-2] = '.';
            FOGout[gesl-1] = FOGbuf[gesl-2];
            FOGout[gesl] = FOGbuf[gesl-1];
            FOGout[gesl+1] = 0;
            }
            else FOGout[0] = 'E';

      Serial.print("$TIROT,");      
      Serial.print(FOGout);
      Serial.print(",A");
      Serial.print("\r\n");
        
      }

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

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Alle

könnt ihr mal kontrollieren, ob der ganze if-else Pallawatsch in loop 
identisch ist zu
void loop()
{
  FOGint = FOG() * 100;

  Serial.print("$TIROT,");      

  if( FOGint >= 0 )
    sprintf( FOGout, "0.%03d", FOGint );
  else
    sprintf( FOGout, "-0.%03d", -FOGint );

  Serial.print(FOGout);

  Serial.print(",A\r\n");
}

Autor: Random ... (thorstendb) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
**LOOL** das ist geil!

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

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

Bestimmt nicht ;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
void loop()
{
  int FOGint = FOG() * 100;

  if( FOGint >= 0 )
    sprintf( FOGout, "$TIROT,0.%03d,A\r\n", FOGint );
  else
    sprintf( FOGout, "$TIROT,-0.%03d,A\r\n", -FOGint );

  Serial.print( FOGout );
}

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

void loop()
{
  FOGint = FOG() * 100;

  Serial.print("$TIROT,");

  if( FOGint >= 0 )
    sprintf( FOGout, "0.%02d", FOGint );
  else
    sprintf( FOGout, "-0.%02d", -FOGint );

  Serial.print(FOGout);
 
  Serial.print(",A\r\n");
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
void loop( void )
{
  sprintf( FOGout, "$TIROT,%.2f,A\r\n", FOG() );
  Serial.print( FOGout );
}

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 )

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

char ReceiveChar()
{
  //
  // Warten bis ein Zeichen da ist
  //
  while( !Serial1.available() )
    ;

  //
  // Zeichen liefern
  //
  return Serial1.read();
}

double FOG(void)
{
  uint8_t i;

  //
  // auf den nächsten Datensatz synchronisieren
  // der vorhergehende ist genau dann zu Ende, wenn ein
  // \n empfangen wird. Danach beginnt der nächste Datensatz
  //
  while( ReceiveChar() != '\n' )
    ;

  //
  // Ein Datensatz sind genau 20 Zeichen
  //
  for( i = 0; i < 20; ++i )
    Data[i] = ReceiveChar();

  //
  // daraus interessiert ein double Wert, der an Position 5 beginnt
  // und aus 7 Zeichen besteht.
  //
  Data[5+7] = '\0';
  return atof( &Data[5] );
}

Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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#Aktivi...

> 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Also musste ich mir was basteln.

Du hättest dir zb auch das hier basteln können
void loop()
{
  FOGint = FOG() * 100;

  Serial.print("$TIROT,");      

  sprintf( FOGout, "%d.%02d", FOGint / 100, FOGint  % 100 );
  Serial.print(FOGout);

  Serial.print(",A\r\n");
}

Moral von der Story: Kenne dein Handwerkszeug!

Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht 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! ;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jürgen Papadopolis (juergenp)
Datum:

Bewertung
0 lesenswert
nicht 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.

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.