Forum: Compiler & IDEs Sring in mehrere Zahlen umwandeln


von Wolfgang E. (wolfgang_e)


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)
1
void uart_gets( char* Buffer, uint8_t MaxLen )  {
2
    uint8_t NextChar;
3
    uint8_t StringLen = 0;
4
    NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
5
6
                                      // Sammle solange Zeichen, bis:
7
                                      // * entweder das String Ende Zeichen kam
8
                                      // * oder das aufnehmende Array voll ist
9
    while( NextChar != ';' && StringLen < MaxLen - 1 ) 
10
      {
11
      *Buffer++ = NextChar;
12
        StringLen++;
13
        NextChar = uart_getc();  // While schleife wird bei Strichpunkt beendet.
14
      }
15
16
                      // Noch ein '\0' anhängen um einen Standard
17
                      // C-String daraus zu machen
18
        *Buffer = '\0';
19
    }
20
21
22
23
//---------------------------------------- Interrupt UART------------------------------------------
24
25
26
ISR (USART_RXC_vect) { 
27
    uint8_t test = 0;
28
    
29
    //rcv_uart_str = 1;
30
    uart_gets(Line, sizeof( Line ) );
31
    uputsnl("jetzt bin ich in der isr");
32
33
    test = atoi(Line[0]);
34
    sprintf( Str_ISR, "ISR: %02d", test );  // Ergebnis ist immer "nix"
35
36
}


Kurz zur HW:
ATMega32 - HTerm aufn PC


Danke für euere Hilfe.

Viele Grüße,
Wolfgang

von Benni L. (sutter_cain)


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.

von Hc Z. (mizch)


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.

von Wolfgang E. (wolfgang_e)


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.
1
waiting = atoi(Line);
2
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...

von Benni L. (sutter_cain)


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

von Marc (Gast)


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:
1
waiting = atoi(&Line[1]) oder
2
waiting = atoi(Line+1)

von Marc (Gast)


Lesenswert?

Sorry, da war ich zu langsam :)

von Hc Z. (mizch)


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.

von Markus B. (rusticus)


Lesenswert?

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

mfg

von Imon (Gast)


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.

1
inline uint8_t get_digit_dirty( char digit)
2
{
3
      return digit - (uint8_t) '0';
4
}
5
6
inline uint8_t get_digit( char digit)
7
{
8
      uint8_t retval;
9
10
      switch(digit) {
11
           case '0': retval=0; break;
12
           case '1': retval=1; break;
13
           case '2': retval=2; break;
14
           case '3': retval=3; break;
15
           case '4': retval=4; break;
16
           case '5': retval=5; break;
17
           case '6': retval=6; break;
18
           case '7': retval=7; break;
19
           case '8': retval=8; break;
20
           case '9': retval=9; break;
21
           default: /* Fehler keine Zahl */
22
      };
23
      return retval;
24
}
25
26
void bla( char * Sting ) 
27
{
28
     uint8_t index;
29
     uint8_t value;
30
31
   index = get_digit( String[0]);
32
   value = get_digit( String[1]) * 10 + get_digit(String[2]);
33
}

von U.R. Schmitt (Gast)


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.

von Detlev T. (detlevt)


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:
1
var_index = buffer[0] - '0';
2
var_value = atoi(buffer+1);
Die Werte sollte man dann noch unbedingt auf Konsistenz prüfen.

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.