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
ASCII2Hex Konvertierung
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".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;
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.
Eher so: 'xV4\x12\xef\xcd\xab\t' (zumindest bei x86 Rechnern)Und schließlich im Python String:
string= "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI '\0'"
Kommt Dein abschliessendes Nullbyte von der Übertragung?
Ungefähr so etwas?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'"
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'
-
- User
- Beiträge: 13
- Registriert: Montag 21. März 2005, 15:50
Hi,
danke für die Antwort.
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.
Gruß
chronical_chaos
danke für die Antwort.
Die Länge von (unsigned) long sind 4-Byte und der uC verwendet das Little Endian Format.Also Du müsstest auf jeden Fall erst einmal wissen wie lang ein `long` ist und in welcher Reihenfolge er kodiert ist.
Du hast fast recht: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)
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.
Ja. Bevor ich die Daten übertrage schließe ich den String ab.Kommt Dein abschliessendes Nullbyte von der Übertragung?
Kannst Du mir bitte erklären was Du da machst?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'
Gruß
chronical_chaos
Okay, die Länge und das Format stimmen also auf dem PC überein.chronical_chaos hat geschrieben:Die Länge von (unsigned) long sind 4-Byte und der uC verwendet das Little Endian Format.Also Du müsstest auf jeden Fall erst einmal wissen wie lang ein `long` ist und in welcher Reihenfolge er kodiert ist.
Okay, das sind 6 Bytes. Wir interessieren uns nur für die ersten 4 und die sind Big Endian!Du hast fast recht: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)
0x1234567 sieht im string (Ausgabe mittels repr(string)) so aus:
'\x124Vx\x00\x1b'
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 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.
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.Ja. Bevor ich die Daten übertrage schließe ich den String ab.Kommt Dein abschliessendes Nullbyte von der Übertragung?
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.Kannst Du mir bitte erklären was Du da machst?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'
-
- 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
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
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.
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.
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 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.