ASCII2Hex Konvertierung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
chronical_chaos
User
Beiträge: 13
Registriert: Montag 21. März 2005, 15:50

Hallo NG,

ich habe folgende Frage, vielleicht kann mir ja jemand helfen.

Kurz zum Hintergrund:
Ich schreibe gerade meine Diplomarbeit in welcher ich mit einem Microcontroller Daten aufzeichne und auswerten will.

Diese Daten entsprechen dem C-Datentyp unsigned long.
Nach der Aufzeichnung führe ich einen Typcast von unsigned long in char durch und übertrage die Daten mittels RS-232 an einen PC.

Am PC liest ein Python Progrämmchen die Daten von RS-232 in einen String ein.

Hier ein Beispiel:
unsigned long Werte im Controller:
ul_Wert1= 0x12345678;
ul_Wert2= 0x9ABCDEF;

Nach dem Typcast sehen die Werte folgendermaßen aus:
c_Wert1= NUL SOH STX ETX EOT ENQ ACK BEL BS;
c_Wert2= TAB LF VT FF CR SO SI;

Und schließlich im Python String:
string= "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI '\0'"

Um die Daten weiterverarbeiten zu können benötige ich wieder die ursprünglichen Werte als Hex Zahlen. Ich möchte nun aber keinen Typcast wie zuvor durchführen, sondern die ASCII Zeichen im String durch die entsprechenden Hex Werte ersetzen.

Das würde dann so aussehen:

string= "012345678ABCDEF'\0'"

Da meine Python Kenntnisse äußerst beschränkt sind, wäre ich für jeden Tip sehr dankbar.

MfG

chronical_chaos
BlackJack

chronical_chaos hat geschrieben:Diese Daten entsprechen dem C-Datentyp unsigned long.
Nach der Aufzeichnung führe ich einen Typcast von unsigned long in char durch und übertrage die Daten mittels RS-232 an einen PC.

Am PC liest ein Python Progrämmchen die Daten von RS-232 in einen String ein.

Hier ein Beispiel:
unsigned long Werte im Controller:
ul_Wert1= 0x12345678;
ul_Wert2= 0x9ABCDEF;

Nach dem Typcast sehen die Werte folgendermaßen aus:
c_Wert1= NUL SOH STX ETX EOT ENQ ACK BEL BS;
c_Wert2= TAB LF VT FF CR SO SI;
Das glaube ich nicht. Die Werte der ASCII-Zeichen die Du angibst, stimmen in keiner Weise mit den Zahlen (long) darüber überein. Die Zahlen haben nur 4 Bytes, d.h. wenn `long` auch nur 4 Bytes gross ist, dann haben die "c_Werte" nur 4 Bytes, wenn `long` allerdings 8 Bytes gross ist, dann hast Du auf jeden Fall 4-mal "\0" in jedem "c_Wert".

Dann wäre noch die Frage wo die stehen und in welcher Reihenfolge die Bytes da stehen, das heisst ob der Microcontroller Big Endian oder Little Endian arbeitet.

Also Du müsstest auf jeden Fall erst einmal wissen wie lang ein `long` ist und in welcher Reihenfolge er kodiert ist.

Auf x86 Rechnern ist `long` im allgemeinen genau wie `int` 4 Bytes lang und die niederwertigen Bytes kommen zuerst, also wird aus 0x12345678 die Zeichenkette: 'xV4\x12'. Drei der vier Bytes sind also sogar darstellbare ASCII-Zeichen.
Und schließlich im Python String:
string= "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI '\0'"
Eher so: 'xV4\x12\xef\xcd\xab\t' (zumindest bei x86 Rechnern)

Kommt Dein abschliessendes Nullbyte von der Übertragung?
Um die Daten weiterverarbeiten zu können benötige ich wieder die ursprünglichen Werte als Hex Zahlen. Ich möchte nun aber keinen Typcast wie zuvor durchführen, sondern die ASCII Zeichen im String durch die entsprechenden Hex Werte ersetzen.

Das würde dann so aussehen:

string= "012345678ABCDEF'\0'"
Ungefähr so etwas?

Code: Alles auswählen

In [16]: a
Out[16]: 'xV4\x12\xef\xcd\xab\t'

In [17]: ''.join('%08x' % i for i in struct.unpack('LL', a))
Out[17]: '1234567809abcdef'
chronical_chaos
User
Beiträge: 13
Registriert: Montag 21. März 2005, 15:50

Hi,

danke für die Antwort.
Also Du müsstest auf jeden Fall erst einmal wissen wie lang ein `long` ist und in welcher Reihenfolge er kodiert ist.
Die Länge von (unsigned) long sind 4-Byte und der uC verwendet das Little Endian Format.
Zitat:
Und schließlich im Python String:
string= "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI '\0'"


Eher so: 'xV4\x12\xef\xcd\xab\t' (zumindest bei x86 Rechnern)
Du hast fast recht:
0x1234567 sieht im string (Ausgabe mittels repr(string)) so aus:
'\x124Vx\x00\x1b'

Ich habe die andere Darstellung genommen weil ich die ASCII Tabelle grade zu Hand hatte.
Kommt Dein abschliessendes Nullbyte von der Übertragung?
Ja. Bevor ich die Daten übertrage schließe ich den String ab.
Python-Code:
In [16]: a
Out[16]: 'xV4\x12\xef\xcd\xab\t'

In [17]: ''.join('%08x' % i for i in struct.unpack('LL', a))
Out[17]: '1234567809abcdef'
Kannst Du mir bitte erklären was Du da machst?

Gruß

chronical_chaos
BlackJack

chronical_chaos hat geschrieben:
Also Du müsstest auf jeden Fall erst einmal wissen wie lang ein `long` ist und in welcher Reihenfolge er kodiert ist.
Die Länge von (unsigned) long sind 4-Byte und der uC verwendet das Little Endian Format.
Okay, die Länge und das Format stimmen also auf dem PC überein.
Zitat:
Und schließlich im Python String:
string= "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI '\0'"


Eher so: 'xV4\x12\xef\xcd\xab\t' (zumindest bei x86 Rechnern)
Du hast fast recht:
0x1234567 sieht im string (Ausgabe mittels repr(string)) so aus:
'\x124Vx\x00\x1b'
Okay, das sind 6 Bytes. Wir interessieren uns nur für die ersten 4 und die sind Big Endian!

Code: Alles auswählen

In [34]: a
Out[34]: '\x124Vx\x00\x1b'

In [35]: struct.unpack('>L', a[:4])
Out[35]: (305419896L,)

In [36]: '%08x' % struct.unpack('>L', a[:4])[0]
Out[36]: '12345678'
In `a` steht die Zeichenkette, mit `struct.unpack` konvertiere ich die ersten 4 Bytes der Zeichenkette in ein Big Endian unsinged long ('>L').

In der letzten Zeile nehme ich die Zahl aus dem Ergebnistupel und formatiere sie mit führenden 0en auf 8 Zeichen aufgefüllt als Hexadezimalzahl.

Das Auffüllen mit 0en sieht man im Beispiel natürlich nicht, weil die Zahl gross genug ist, das alle 8 Ziffern != 0 sind. Aber wenn Du, wie Du sagst, zwei solcher Werte verketten möchtest, dann muss man dafür sorgen, das beide gleichlang sind, damit das Ergebnis eindeutig ist.
Kommt Dein abschliessendes Nullbyte von der Übertragung?
Ja. Bevor ich die Daten übertrage schließe ich den String ab.
Ich hoffe das benutzt Du nicht als Endmarkierung für die Übertragung. Nullbytes können schliesslich auch in ganz normalen Integerzahlen vorkommen. Es kann jeder Bytewert vorkommen, also kann man sich nur an der Anzahl der übertragenen Bytes orientieren.
Python-Code:
In [16]: a
Out[16]: 'xV4\x12\xef\xcd\xab\t'

In [17]: ''.join('%08x' % i for i in struct.unpack('LL', a))
Out[17]: '1234567809abcdef'
Kannst Du mir bitte erklären was Du da machst?
Im Grunde das gleiche wie oben, nur das ich zwei `unsigned long` ohne Endian-Angabe (also auf dem PC Little Endian) aus einer 8 Byte langen Bytekette konvertiere, als Hexadezimalzahl formatiere und die beiden Zeichenketten dann verbinde.
chronical_chaos
User
Beiträge: 13
Registriert: Montag 21. März 2005, 15:50

Danke für die Erklärung.

Ich habs allerdings mittlerweile in C gelöst.

Wen es interessiert die Idee war folgende:

Ich sehe mir jedes Byte im char Array an und prüfe ob sein Hex Wert <= 0x0F ist. Falls diese Bedingung zutrifft kann eine direkte Substitution in ein neu angelegtes char Array erfolgen.

zum Beispiel:
\x0 --> \x30
\x1 --> \x31
.
.
.
\xF --> \x46

Für Werte größer 0x0F also Werte ab 0x10 Weise ich das High und Low Nibble jeweils einer eigenen Variable zu.
Das High Nibble wird um 4 Stellen nach rechts geshiftet und anschließend wieder durch den korrespondierenden Wert substituiert.
Mit dem Low Nibble verfahre ich analog.
Anschließend wird der aus dem High Nibble entstandende Wert dem neuen char Array zugewiesen und danach der Low Nibble Wert.

z.B.: \x12 soll umgewandelt werden

High Nibble= 0x10
High Nibble >> 4= 0x01 --> \x31

Low Nibble= 0x02 --> \x32

=> neues c_Array[]= {..., '\x31', \x32',...,'\0'}

Thats it.

Trotzdem vielen Dank für Eure Hilfe!

Gruß

chronical_chaos
BlackJack

Du musst immer die oberen 4 Bit berücksichtigen, auch wenn die 0 sind, dann muss dafür eine '0' im Ergebnis stehen. Also brauchst Du auch nicht prüfen ob der Wert kleiner als 16 ist, sondern kannst immer das aufteilen in High- und Low-Nibble praktizieren.

Code: Alles auswählen

#include <stdio.h>

static const char *hextab = "0123456789abcdef";

void mem2hex_str(char *target, void *source, int source_length)
{
    char *source_ptr = (char *) source;
    
    while (source_length) {
        *target++ = hextab[*source_ptr >> 4];
        *target++ = hextab[*source_ptr & 0x0f];
        ++source_ptr;
        --source_length;
    }
    *target = '\0';
}

void big_endian2hex_str(char *target, void *source, int source_length)
{
    char *source_ptr = (char *) source;
    
    while (source_length) {
        --source_length;
        *target++ = hextab[source_ptr[source_length] >> 4];
        *target++ = hextab[source_ptr[source_length] & 0x0f];
    }
    *target = '\0';
}

#define be_long2hex_str(value, target) \
                            (big_endian2hex_str(target, value, sizeof(long)))

void main(void)
{
    unsigned long test_long = 0x12345678;
    char hex_str[sizeof(unsigned long) * 2 + 1];
    
    mem2hex_str(hex_str, &test_long, sizeof(unsigned long));
    puts(hex_str);
    
    be_long2hex_str(&test_long, hex_str);
    puts(hex_str);
    
    sprintf(hex_str, "%08lx", test_long);
    puts(hex_str);
}
Die erste Funktion sollte auf Deinem Mikrokontroller funktionieren, weil der Big Endian arbeitet. Es wird einfach eine angegebene Anzahl von Bytes in Hex-Ziffern umgewandelt und in den angegebenen Speicherbereich geschrieben.

Die zweite Funktion macht das gleiche, nur das die Bytes rückwärts gelesen werden, also das was man auf Little Endian Rechnern machen müsste.

Und als letztes ist in der `main()`-Funktion noch die einfachste Variante, die analog zu meinem Python-Beispiel funktioniert: Man lässt die Zahl von der C-Bibliothek als Hexadezimalzahl mit 8 Ziffern und führenden 0en formatieren. Im Gegensatz zu Python muss man in der Format-Zeichenkette noch ein 'l' vor das 'x' schreiben wenn man ein `long` statt eines `int` benutzt.
Antworten