www.mikrocontroller.net

Forum: Compiler & IDEs Sring in mehrere Zahlen umwandeln


Autor: Wolfgang E. (wolfgang_e)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo liebe Hacker ;-)

Ich habe folgendes - wahrscheinlich suuper einfaches - "String" Problem.

Ich möchte gerne UART als "Kalibrationsmodul" für meine 
Wintergartentemperaturregelung verwenden.

Dazu möchte ich gerne einen kurzen String - bestehend nur aus Zahlen via 
UART in den uC einlesen, und diesen dann auswerten. Zeichen werden über 
ISR eingelesen. UART Kommunikation funktioniert auch so weit, aber es 
scheitert an der anschließenden Weiterverarbeitung.

Länge des Strings ist immer gleich - Position der Daten auch. Endzeichen 
ist ";".

An der ersten Position im String wird die zu kalibrierende Variable 
stehen.
An der 2. und 3. Position die neue Temperatur.

Beispiel:

123;  Variable 1 23Grad
245;  Variable 2 45Grad
Wie trenne ich jetzt am besten den String auf.

Ziel wäre es, das in der ISR  die Auswertung gemacht wird, und die Daten 
anschließend gleich in den EEPROM geschrieben werden. Ausserdem wird ein 
"NewValueAvialable" Flag gesetzt, da die MainRoutine veranlasst, sich 
die neuen Daten aus dem EEPROM zu holen.....



Folgenden Code hab schon programmiert (bzw. abgekuckt)
void uart_gets( char* Buffer, uint8_t MaxLen )  {
    uint8_t NextChar;
    uint8_t StringLen = 0;
    NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen

                                      // Sammle solange Zeichen, bis:
                                      // * entweder das String Ende Zeichen kam
                                      // * oder das aufnehmende Array voll ist
    while( NextChar != ';' && StringLen < MaxLen - 1 ) 
      {
      *Buffer++ = NextChar;
        StringLen++;
        NextChar = uart_getc();  // While schleife wird bei Strichpunkt beendet.
      }

                      // Noch ein '\0' anhängen um einen Standard
                      // C-String daraus zu machen
        *Buffer = '\0';
    }



//---------------------------------------- Interrupt UART------------------------------------------


ISR (USART_RXC_vect) { 
    uint8_t test = 0;
    
    //rcv_uart_str = 1;
    uart_gets(Line, sizeof( Line ) );
    uputsnl("jetzt bin ich in der isr");

    test = atoi(Line[0]);
    sprintf( Str_ISR, "ISR: %02d", test );  // Ergebnis ist immer "nix"

}


Kurz zur HW:
ATMega32 - HTerm aufn PC


Danke für euere Hilfe.

Viele Grüße,
Wolfgang

Autor: Benni L. (sutter_cain)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sieht so aus, als wäre atoi(char *string) dein Freund.

Wenn du danach ein wenig suchst, dürftest du schnell fündig werden.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe da zunächst noch ein anderes Problem.

Der UART-Rx-Interrupt kommt pro empfangenem Zeichen.  Es ist sinnlos, 
darin einen gesamten String (also mehr als 1 Zeichen) abholen zu wollen 
und genauso, darin einen ganzen String abzusenden.  Das solltest Du 
besser zunächst umschreiben (Ansätze für UART-Rx-Interruptroutinen 
gibt's hier viele zum Nachlesen), bevor Du Dich an die Verarbeitung des 
empfangenen Strings machst.

Autor: Wolfgang E. (wolfgang_e)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Benni L. schrieb:
> Sieht so aus, als wäre atoi(char *string) dein Freund.
>
> Wenn du danach ein wenig suchst, dürftest du schnell fündig werden.

Ich hab schon ein wenig mit atoi rumprobiert...


Solange ich den ganzen String mit atoi bearbeite funktionierts 
wunderbar.
waiting = atoi(Line);
printf("\n wartezeit %i \n",waiting);

Verwende ich aber atoi(Line[1]) gehts nicht mehr.

Das die ISR bei jedem neuen Zeichen ausgeführt wird kann ich mir ehrlich 
gesagt nicht vorstellen, den die obrigen Zeilen waren schon in der ISR 
drinn, und das hat wunderbar fuktioniert.

Aber falls es wirklich so sein sollte, dann kann ich das ganze auch in 
der Main machen...

Autor: Benni L. (sutter_cain)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bin grad schon ein wenig zu müde um mich mehr reinzudenken.

Aber probier doch einfach sprintf("%s",Line) mal aus, dann siehst du wie 
viele Zeichen in Line sind.

Und was willst du mit atoi(Line[0]), das funktioniert natürlich nicht. 
Da atoi einen Pointer erwartet und dann den String durchgeht bis zum 
Ende des String. Evtl. funktioniert atoi(&Line[0]) bzw. dann halt gleich 
atoi(Line).

Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du solltest der Funktion atoi(const char* s) einen String übergeben und 
nicht ein char. Durch Line[1] wird ein char übergeben und dieser in 
einen Zeiger auf einen String konvertiert.

Der Compiler wirft sicher eine Warning:

warning: passing argument 1 of 'atoi' makes pointer from integer without 
a cast

Also übergib den Zeiger auf den String an atoi:
waiting = atoi(&Line[1]) oder
waiting = atoi(Line+1)

Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, da war ich zu langsam :)

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang E. schrieb:
> Das die ISR bei jedem neuen Zeichen ausgeführt wird kann ich mir ehrlich
> gesagt nicht vorstellen,

Das Datenblatt hilft hier weiter.

> den die obrigen Zeilen waren schon in der ISR
> drinn, und das hat wunderbar fuktioniert.

Mag sein, wenn ein paar Randbedingungen zutreffen.  Da Du nur ein paar 
Ausschnitte und nicht das komplette Programm gezeigt hast, lässt sich 
das nicht beurteilen.  So der Richtung nach würde ich sagen, dass 
mindestens jeweils das erste Zeichen fehlen dürfte.  Der Sinn einer 
Interruptroutine ist ohnehin verfehlt, wenn darin gewartet wird, z.B. 
auf das nächste Zeichen.

Aber aus nichts lernt man besser als aus den eigenen Fehlern (heißt es). 
Ich lasse es also.

Autor: Markus B. (rusticus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schon mal probiert zu rechnen?
Bin mir jetzt nicht sicher aber ich glaube das du das folgendermaßen 
lösen könntest:
zahl = atoi (uart_string);  //Da steht nun 1xx oder 2xx drin
varInt = zahl % 100 // Gibt dir deine Temperatur
varInt2 = zahl / 100 //Da Int, schneidet Kommazahlen ab, hast du die Variablen

mfg

Autor: Imon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

wenn ich dich richtig verstanden habe ist die erste stelle deines String 
ein
Index und die nächsten 2 der wert welcher in den index geschrieben 
werden soll.Du must also nur aus den einzelen Zeichen in den String eine 
Zahl machen und einmal mit 10 Multiplizieren. Die Quick& Dirty lösung 
die mir sofort einfallt ist von den jeweiligen Zeichen '0' abziehen.
Ist zwar super dreckig aber die Erfahrung zeigt das es geht. Wenn du es 
eine Stufe Sauber machen willst macht du ein Switch Case für das 
jeweilige Zeichen. Dann must du nur noch (tm) bei deiner Grad Celsius 
Rechnung die Zahl die du in dem Zehner teil mit der 10 multiplizieren.

inline uint8_t get_digit_dirty( char digit)
{
      return digit - (uint8_t) '0';
}

inline uint8_t get_digit( char digit)
{
      uint8_t retval;

      switch(digit) {
           case '0': retval=0; break;
           case '1': retval=1; break;
           case '2': retval=2; break;
           case '3': retval=3; break;
           case '4': retval=4; break;
           case '5': retval=5; break;
           case '6': retval=6; break;
           case '7': retval=7; break;
           case '8': retval=8; break;
           case '9': retval=9; break;
           default: /* Fehler keine Zahl */
      };
      return retval;
}

void bla( char * Sting ) 
{
     uint8_t index;
     uint8_t value;

   index = get_digit( String[0]);
   value = get_digit( String[1]) * 10 + get_digit(String[2]);
}


Autor: U.R. Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang E. schrieb:
> Länge des Strings ist immer gleich - Position der Daten auch. Endzeichen
> ist ";".
>
> An der ersten Position im String wird die zu kalibrierende Variable
> stehen.
> An der 2. und 3. Position die neue Temperatur.
>
> Beispiel:
>
> 123;  Variable 1 23Grad
> 245;  Variable 2 45Grad
> Wie trenne ich jetzt am besten den String auf.

Wenn ich Dich recht verstehe, dann baust Du dir den String selbst auf. 
Also könntest Du Dein Protokoll etwas besser bzgl. der Auswertung 
gestalten.

Was hindert dich daran z.B. zu schreiben:
1:26;
oder 12=142;
Jetzt kannst Du einfach den String einlesen, bis Du auf ein Doppelpunkt, 
bzw. ein '=' stösst und weisst genau, daß der erste Teil die Nummer der 
Variablen ist und der folgende Teil der Wert. Du speicherst beim Lesen 
aus der seriellen Schnittstelle schon beide Teile in verschiedenen 
Strings ab und kannst die dann mit atoi oder atol einfach konvertieren. 
Auch könntest Du dann sowohl Deine Variablen statt mit Ziffern mit Namen 
versehen und als Werte auch String senden wenn das mal nötig sein 
sollte.

Was deine Progammierkenntnisse betrifft. Jeder fängt mal an, aber man 
lernt nicht indem man Code aus dem internet zusammensucht, sondern indem 
man sich eine bewältigbare Aufgabe stellt und die dann selbstständig 
löst. Stringverarbeitung/Zerlegung ist eines der Basics, das solltest Du 
selbstständig können.
Also nimm dir so ein Beispiel und programmiere das auf dem PC bis es 
funktioniert. Dort kannst Du viel schneller ausprobieren und besser 
debuggen als auf dem µC. Wenn es dann grundlegend funktioniert portierst 
Du den Code in dein µC Programm.
Viel Spass am selbst programmieren.

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin der Meinung: "In die ISR immer nur das allernötigste". Ich würde 
da eine zusätzliche Auswerteroutine nehmen, die vom Hauptprogramm 
ausgerufen wird, wenn Zeit ist. Die sammelt die Zeichen ein (lokale, 
statische(!) Variable) und startet eine Auswertung, wenn das Ende 
erkannt wird.

Das könnte dann so aussehen:
var_index = buffer[0] - '0';
var_value = atoi(buffer+1);
Die Werte sollte man dann noch unbedingt auf Konsistenz prüfen.

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.