Forum: Mikrocontroller und Digitale Elektronik ASCI Low Byte + High Byte zu char umrechnen


von Sunny (Gast)


Lesenswert?

Hallo Gemeinde,

ich stehe gerade irgendwie auf dem Schlauch...

In einer Modbus-Anwendung wird der HEX Wert in Form von ASCI Zeichen 
übertragen....

Beispiel:

unsigned char zeichen = 0x48;

wird übertragen  als ['4','8'].

Wenn ich das Modbus Telegram nun wieder einlese, möchte diese beiden 
Bytes wieder zu einem 0x48 umwandeln... aber wie?

Kann mir vielleicht jemand auf die Sprünge helfen?

LG, sunny

von g457 (Gast)


Lesenswert?

> Kann mir vielleicht jemand auf die Sprünge helfen?

man strtol. Kann man aber auch als Dreizeiler selber implementieren.

von Karl M. (Gast)


Lesenswert?

Hallo,

ziehen jeweils von jedem Zeichen der Dezimalwert von '0' ab und addiere 
die Ziffer mit ihrer Wertigkeit und addiere sie.
Zahl a = 10^1*a1 + 10^0*a0

Jede dezimal Zahl x lässt sich auf diese Art und Weise auf Basis von 
*10^n* darstellen!

von Karl M. (Gast)


Lesenswert?

Sorry,

die Basis in deiner Zahlendarstellung ist natürlich 16, also ersetze 
bitte 10^n durch 16^n.

von Joe F. (easylife)


Lesenswert?

Karl M. schrieb:
> ziehen jeweils von jedem Zeichen der Dezimalwert von '0' ab

Gibt dann natürlich auch noch a-f und/oder A-F...

von Sunny (Gast)


Lesenswert?

Hey,

vielen Dank für eure Antworten.
Ich habe das mal ausprobiert und erhalte leider nicht das Ergebnis, was 
ich erhofft habe.

Was mache ich denn wohl falsch?
1
#include <stdio.h>
2
3
4
int main(void) {
5
6
        unsigned char vergleich = 0x48;
7
        unsigned char modbus[] = {'4','8'};
8
9
        modbus[0] -= '0';
10
        modbus[1] -= '0';
11
12
        unsigned char wert = 16^1 * modbus[0] + 16^0 * modbus[1];
13
14
        printf("vergleich = %d\n", vergleich);
15
        printf("     wert = %d\n", wert);
16
17
        return(0);
18
}

Ausgabe:
1
vergleich = 72
2
     wert = 4

von Joe F. (easylife)


Lesenswert?

^ ist XOR

1
#include <stdint.h>
2
#include <stdio.h>
3
4
uint32_t hex2bin(char *str, uint8_t digits)
5
{
6
  uint32_t val = 0;
7
  int      i;
8
  
9
  for (i=0; i<digits; i++)
10
  {
11
    val <<= 4;
12
    
13
    if (*str >= 'A')
14
      val |= *str - 'A' + 10;
15
    else if (*str >= 'a')
16
      val |= *str - 'a' + 10;
17
    else
18
      val |= *str - '0';
19
      
20
    str++;
21
  }
22
  return val;
23
}      
24
  
25
  
26
27
int main(void) 
28
{
29
        unsigned char vergleich = 0xba;
30
        char          modbus[] = {'b','A'};
31
        
32
        unsigned char wert = hex2bin(modbus, 2);
33
34
        printf("vergleich = %d\n", vergleich);
35
        printf("     wert = %d\n", wert);
36
37
        return(0);
38
}

Funktioniert bis 32-bittige Werte (8 digits), und sowohl für Groß- als 
auch Kleinschreibung.
Großartige Plausibilitätsprüfung wird zugunsten der Perfomance nicht 
gemacht.
"G7" ergäbe natürlich Unsinn.

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Sunny schrieb:
> Ich habe das mal ausprobiert und erhalte leider nicht das Ergebnis, was
> ich erhofft habe.

Guck dir mal als Grundlage in einer ASCII-Tabelle die Werte der Ziffern 
'0'..'9' und die der Buchstaben 'A'..'F' bzw. 'a'..'f' an.

Die Umwandlung Klein- nach Großbuchstaben kann man einfach durch 
Maskierung des 0x20-Bits machen, ganz ohne große Fallunterscheidung.

Aus

Joe F. schrieb:
> if (*str >= 'A')
>       val |= *str - 'A' + 10;
>     else if (*str >= 'a')
>       val |= *str - 'a' + 10;

wird dann

if (*str >= 'A')
    val |= (*str & ~0x20) - 'A' + 10;

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wolfgang schrieb:
> Die Umwandlung Klein- nach Großbuchstaben kann man einfach durch
> Maskierung des 0x20-Bits machen, ganz ohne große Fallunterscheidung.

Dafür gibt es in der C-Umgebung auch die Macros tolower und toupper.

von Joe F. (easylife)


Lesenswert?

Rufus Τ. F. schrieb:
> die Macros tolower und toupper

Bin gar nicht sicher, ob das wirklich Macros sind.
Schneller als ein if() werden die auch nicht sein, und im ungünstigsten 
Fall knallen die eine ziemlich große Lookuptable in die Firmware.

Hatte allerdings noch einen Fehler in hex2bin, denn 'a'-'z' hat höhere 
Ascii Werte als 'A'-'Z'.

Wolfgang schlug ein schnelles toupper() vor, genauso einfach wäre dann 
ein tolower(), indem man das 6. Bit auf 1 zwingt.
1
uint32_t hex2bin(char *str, uint8_t digits)
2
{
3
  uint32_t val = 0;
4
  int      i;
5
  
6
  for (i=0; i<digits; i++)
7
  {
8
    val <<= 4;
9
    
10
    if (*str <= '9')
11
      val |= *str - '0';
12
    else
13
      val |= (*str | 0x20) - 'a' + 10;
14
      
15
    str++;
16
  }
17
  return val;
18
}

: Bearbeitet durch User
von fop (Gast)


Lesenswert?

Sunny schrieb:
> unsigned char wert = 16^1 * modbus[0] + 16^0 * modbus[1];

Hier klafft die Schreibweise von Basic und C auseinander. Der Dir den 
Vorschlag gemacht hat, meinte 16 hoch 1 also. In Basic schreibt man das 
auch 16^1. In C bedeutet ^ jedoch bitweises exklusiv oder.
  0
16  = 1

  1
16  = 16

16 exklusiv oder 0 = 16
16 exklusiv oder 1 = 17
das ergibt natürlich nur Grütze.
1
unsigned char wert = 16 * modbus[0] + modbus[1];

Denke ich, kann man schreiben, ohne es so stark vereinfacht zu haben, 
dass es niemand mehr nachvollziehen kann.

von Sunny (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für eure Antworten.

Da ich Kleinbuchstaben im Telegramm nicht erhalte, habe ich mir nun mit 
Eurer Hilfe folgende Funktion geschrieben:

1
#include <stdio.h>
2
3
unsigned char chr2val(unsigned char);
4
5
int main(void) {
6
7
        unsigned char vergleich = 0xFF;
8
        unsigned char modbus[] = {'F','F'};
9
10
        unsigned char wert = chr2val(modbus[0]) * 16 + chr2val(modbus[1]);
11
12
        printf("vergleich = %d\n", vergleich);
13
        printf("     wert = %d\n", wert);
14
        return(0);
15
}
16
17
unsigned char chr2val(unsigned char c) {
18
19
    if (c >= '0' && c <= '9')
20
        return(c - '0');
21
22
    if (c >= 'A' && c <= 'F')
23
        return(10 + c - 'A');
24
25
    return(0);
26
}

Damit funktioniert das Vergleichen der Werte prima.

Besten Dank nochmal - Sunny

von Wolfgang (Gast)


Lesenswert?

Sunny schrieb:
> Damit funktioniert das Vergleichen der Werte prima.

Was bringen dir die ganzen Vergleiche?

Wenn die Daten ok sind, kommen keine anderen Zeichen vor und wenn die 
Daten nicht ok sind, gibst du fehlerhafte Daten aus deiner Funktion 
zurück, ohne es zu merken. Wenn du schon die anderen Fälle aussortierst, 
sollte ein Fehlerzähler hochlaufen, damit man merkt, dass die 
zurückgegebenen 0-en falsch sind. Oder ist in deinen Modbustelegrammen 
ausgeschlossen, dass der Fehlerwert 0 auch als übertragene "0" vorkommt?

von Joe F. (easylife)


Lesenswert?

Sunny schrieb:
> Da ich Kleinbuchstaben im Telegramm nicht erhalte

Na, dann hätte ich noch diesen Vorschlag für ein Macro mit einer 
Lookup-Table.
Da zwischen Zahlen und Großbuchstaben nur 7 Zeichen liegen, ist die 
Speicherplatzverschwendung vernachlässigbar.
1
#include <stdio.h>
2
#include <stdint.h>
3
4
const uint8_t h2b_lookup[] = 
5
{ 
6
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
7
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8
  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F 
9
};
10
11
#define hex2bin(str) (h2b_lookup[str[0]-'0'] << 4 | h2b_lookup[str[1]-'0'])
12
13
int main(void)
14
{
15
  int i;
16
  char str[3];
17
  
18
  for (i=0; i<256; i++) {
19
    sprintf(str, "%02X", i);
20
    printf("string: %s  hex2bin: %02X\n", str, hex2bin(str));
21
  }
22
  return 0;
23
}

von Sunny (Gast)


Lesenswert?

Hallo,

@Joe: Das sieht auch interessant und übersichtlich aus... werde das mal 
ausprobieren und dann evtl. die Funktion durch das Makro ersetzen.

@Wolfgang:
Im Grunde ist mir der Inhalt des Modbus Telegrams garnicht so wichtig, 
weil ich eigentlich nur wissen will, ob überhaupt ein vollständiges 
Telegram angekommen ist.
Allerdings ist im Telegramm eine LRC Summe enthalten, die ich noch 
prüfen will, bevor ich die Daten weiterreiche.

Das LRC wird ebenfalls Form von 2 ASCII Zeichen übertragen, z.B. ['3', 
'F'], die aber ein 0x3F repräsentieren...

Wenn also das Modbus Telegram z.B. so aussieht:

:0103000600043F[CR][LF] (alles ASCII Zeichen, wohlbemerkt)

Dann muss ich ja selbst auch ein LRC bilden um es mit dem übertragenen 
LRC vergleichen zu können.

Dafür die Umrechnung der ASCII Zeichen in entsprechende hex Werte.

Sollte ein Byte im Telegramm kaputt sein und die chr2val() Funktion eine 
0 zurückgeben, würde ja auch der LRC Check fehlschlagen und das 
Telegramm wird aussortiert.

So zumindest meine Theorie ;)

LG, Sunny

Beitrag #4954894 wurde vom Autor gelöscht.
von Joe F. (easylife)


Lesenswert?

Sunny schrieb:
> @Joe: Das sieht auch interessant und übersichtlich aus... werde das mal
> ausprobieren

Der Nachteil dieser Lösung ist, sie setzt voraus, dass keine unerlaubten 
Zeichen verwendet werden. Ansonsten entstehen Array-Indizes, die 
ausserhalb des Arrays liegen. Kann gutgehen, kann aber auch das Programm 
zum Absturz bringen.

Da bei einer Datenübertragung immer auch mit Fehlern gerechnet werden 
muss, hier nochmal eine Alternative der Makro-Lösung, die zumindest das 
Programm nicht crahed.
Bei 'illegalen' Zeichen kommt dann einfach ein falscher Wert raus.

1
const uint8_t h2b_lookup[32] = 
2
{ 
3
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
4
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5
  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
6
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 
7
};
8
9
#define hex2bin(str) (h2b_lookup[(str[0]-'0') & 0x1F] << 4 | h2b_lookup[(str[1]-'0') & 0x1F])

: Bearbeitet durch User
von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Wie schon geschrieben wurde, werden von den Zeichen nur der Wert von '0' 
abgezogen.
Wenn das Ergebnis dann noch >9 ist, werden weitere 7 abgezogen.
Wenn das Ergebnis immer noch >15 ist, ziehen wir 32 ab

"0" 0x30
"9" 0x39
"A" 0x41
"F" 0x46
"a" 0x61
"z" 0x66

0-9 wird zu Wert 0...9
A Wird zu Wert 17, ist somit >9, wir ziehen 7 weitere ab -> 10
a wird dadurch aber nur zu 42, ist somit >15, wir ziehen 32 ab -> 10

wenn jetzt immer noch Werte >15 vorliegen, war das Zeichen nicht für uns 
bestimmt ;)


MfG

Wenn ich die Syntax halbwegs gefressen habe, könnte Das passen:
1
 unsigned char chr2val(unsigned char c) {
2
   wert=c-'0'
3
   if (wert>9)
4
      wert=wert-7;
5
      if (wert>15)
6
         wert=wert-32;
7
      }
8
   }
9
   if (wert>15)
10
      wert=0xff;
11
   }
12
   return(wert);
13
 }
Im Fehlerfall liefert die Funktion 0xFF, statt einer 0

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.