ASCI Low Byte + High Byte zu char umrechnen

Gast #4953947
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
Gast #4953956
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!
Gast #4954032
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
#4954033
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.
Gast #4954047
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;
#4954357
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
}
Gast #4954455
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.
Gast #4954640
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
Gast #4954788
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?
#4954832
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
}
Gast #4954887
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 von einem Moderator gelöscht.
#4954908
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])
#4954918
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren