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.
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
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.
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 | }
|
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.
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.
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
@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 | }
|
Karl heinz Buchegger schrieb:
> sprintf( "0.%03d", FOGint );
Bestimmt nicht ;-)
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.
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 | }
|
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 )
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 | }
|
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...
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.
> 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!
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! ;-)
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.
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.
|