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
> Kann mir vielleicht jemand auf die Sprünge helfen?
man strtol. Kann man aber auch als Dreizeiler selber implementieren.
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!
Sorry, die Basis in deiner Zahlendarstellung ist natürlich 16, also ersetze bitte 10^n durch 16^n.
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...
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 |
^ 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
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;
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.
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
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.
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
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?
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 | }
|
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.