# libnodave: Drehen von Bytes nicht immer nötig?!



## poppycock (31 Mai 2011)

Hallo,

ich habe in VB.net eine kleine Anwendung geschrieben, mit der ich einzelne Datenbereiche aus der S7-300 via MPI-RS232-Adapter auslesen kann.
Damit ich sehe, was auf der RS232-Leitung passiert, zeichnet ein Serial-Monitor die Daten auf.

Nun habe ich ein Problem mit der Interpretation der Daten, jedenfalls weiß ich nicht, wann ich Bytes drehen muss und wann nicht.
Ich lese später nur die Bytes ein, die ich dann ein wenig umher schieben muss! Das Zielsystem arbeitet mit little endian.

Dazu zwei Beispiele (die relevanten "Datenbytes" sind fett markiert):

Ich lese das EW0 aus:
... 0xFF 0x04 0x00 0x10 0x10 *0x01* *0x80* 0x10 0x03 ...
Folgende Eingänge sind aktiv: E0.0 und E1.7
Macht in Dual: 00000001 10000000 = 1 und 128 in Dezimal.
Die Bytes werden getauscht und zu einem 16bit-Wert zusammengefasst:
10000000 00000001 = 32769 in Dezimal
*Hier muss getauscht werden!*

Nun lese ich ein INT aus:
... 0xFF 0x04 0x00 0x10 0x10 *0x7F* *0xFF* 0x10 0x03 ...
Als Dezimalwert erwarte ich 32767.
Macht in Dual: 01111111 11111111 = 127 und 255 in Dezimal.
Die Bytes werden getauscht und zu einem 16bit-Wert zusammengefasst:
11111111 01111111 = 65407 in Dezimal
Das passt nicht, da 65407 ungleich 32767 ist!
Drehe ich nicht:
01111111 11111111 = 32767 in Dezimal
*Hier darf nicht getauscht werden!*

Wenn ich mir das INT in dualer Schreibweise angucke, sieht man auf dem ersten Blick, dass man nicht drehen darf, da ich eine positive Zahl erwarte und das höchstwertigste Bit 0 sein muss.

Zu guter Letzt habe ich einen Zähler erstellt, der in einen DB den Zählerstand schreibt.
Als Datentyp im DB habe ich WORD genommen und mir mal in der Online-Beobachtung die Änderung des Wertes im DB angeguckt.
Was aufgefallen ist, ist, dass das zweite Datenbyte zuerst hochgezählt wird, danach das erste Datenbyte.
Also:
Zählerstand 255 = *0x00 0xFF* = 00000000 11111111
Zählerstand 256 = *0x01 0x00* = 00000001 00000000
*Hier darf auch nicht getauscht werden!

*So, warum muss man nun beim Eingangswort die Bytes tauschen und z.B. bei einem Zählwert (als WORD) nicht?

Und allgemein: Wie erkennt man, ob man die "Datenbytes" drehen muss oder nicht?

Gruß,
poppycock


----------



## bits'bytes (31 Mai 2011)

Hallo,

eventuell interpretierst du die Quelle falsch? 
Wenn die Quelle ein Byte ist (also 1 Byte breit) dann nicht tauschen, wenn du 2 x 1 Byte bekommst, nicht tauschen, wenn du 1 x 2 Byte bekommst, dann tauschen.


Tauschen muss du folgendes


```
BYTE (1 Byte) .... nein
WORD (2 Byte)  HB, LB --> LB, HB
INT  (4 Byte) HHB, HB, LB, LLB --> LLB, LB, HB, HHB
```


----------



## poppycock (1 Juni 2011)

*Zielsystem: 8bit-Mikrocontroller*

Hallo bits'bytes!



bits'bytes schrieb:


> eventuell interpretierst du die Quelle falsch?


Da ist was dran! Ich muss ja selber entscheiden, wie ich die Quelle zu interpretieren habe.

_Wichtiger Hinweis: Das Zielsystem ist ein 8bit-Mikrocontroller.
Daher muss ich mit jeweils einem Byte "rechnen" und dieses auch verschieben, wenn nötig!_

Ich habe ein paar Mitschnitte von der seriellen Schnittstelle erstellt.
Die Bytes, die ich zu interpretieren habe, sind farbig und *fett* gekennzeichnet:

_*Eingangsbyte 0:*_

```
Request: 01.06.2011 12:52:35.11864 (+158.8985 seconds)
 02
Answer: 01.06.2011 12:52:35.11864 (+0.0000 seconds)
 10
Request: 01.06.2011 12:52:35.12864 (+0.0100 seconds)
 04 82 80 0C 03 14 F1 01 32 01 00 00 00 00 00 0E 
 00 00 04 01 12 0A 10 10 02 00 01 00 00 81 00 00
 00 10 03 5C
Answer: 01.06.2011 12:52:35.13864 (+0.0100 seconds)
 10 02
Request: 01.06.2011 12:52:35.15864 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:35.16864 (+0.0100 seconds)
 04 82 80 0C 14 03 B0 01 01 10 03 BE
Request: 01.06.2011 12:52:35.17864 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:35.17864 (+0.0000 seconds)
 02
Request: 01.06.2011 12:52:35.17864 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:35.18864 (+0.0100 seconds)
 04 82 80 0C 14 03 F1 01 32 03 00 00 00 00 00 02
 00 05 00 00 04 01 FF 04 00 08 [COLOR=Red][B]01[/B][/COLOR] 10 03 3F
Request: 01.06.2011 12:52:35.19864 (+0.0000 seconds)
 10 02
Answer: 01.06.2011 12:52:35.19864 (+0.0000 seconds)
 10
Request: 01.06.2011 12:52:35.19864 (+0.0000 seconds)
 04 82 80 0C 03 14 B0 01 01 10 03 BE
Answer: 01.06.2011 12:52:35.20864 (+0.0100 seconds)
 10
```
*Hier kann natürlich nicht getauscht werden!*

-

_*Eingangsbyte 1**:*_

```
Request: 01.06.2011 12:52:53.40364 (+17.1947 seconds)
 02
Answer: 01.06.2011 12:52:53.40364 (+0.0000 seconds)
 10
Request: 01.06.2011 12:52:53.41364 (+0.0100 seconds)
 04 82 80 0C 03 14 F1 02 32 01 00 00 00 01 00 0E
 00 00 04 01 12 0A 10 10 02 00 01 00 00 81 00 00
 08 10 03 56
Answer: 01.06.2011 12:52:53.41364 (+0.0000 seconds)
 10 02
Request: 01.06.2011 12:52:53.44364 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:53.44364 (+0.0000 seconds)
 04 82 80 0C 14 03 B0 01 02 10 03 BD
Request: 01.06.2011 12:52:53.45364 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:53.46364 (+0.0100 seconds)
 02
Request: 01.06.2011 12:52:53.46364 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:52:53.46364 (+0.0000 seconds)
 04 82 80 0C 14 03 F1 02 32 03 00 00 00 01 00 02
 00 05 00 00 04 01 FF 04 00 08 [COLOR=DarkOrange][B]80[/B][/COLOR] 10 03 BC
Request: 01.06.2011 12:52:53.47364 (+0.0000 seconds)
 10 02
Answer: 01.06.2011 12:52:53.48364 (+0.0100 seconds)
 10
Request: 01.06.2011 12:52:53.48364 (+0.0000 seconds)
 04 82 80 0C 03 14 B0 01 02 10 03 BD
Answer: 01.06.2011 12:52:53.48364 (+0.0000 seconds)
 10
```
*Auch hier kann natürlich nicht getauscht werden!*

-

_*Eingangswort 0**:*_

```
Request: 01.06.2011 12:53:11.61964 (+18.1361 seconds)
 02
Answer: 01.06.2011 12:53:11.62964 (+0.0100 seconds)
 10
Request: 01.06.2011 12:53:11.63964 (+0.0100 seconds)
 04 82 80 0C 03 14 F1 03 32 01 00 00 00 02 00 0E
 00 00 04 01 12 0A 10 10 02 00 02 00 00 81 00 00
 00 10 03 5F
Answer: 01.06.2011 12:53:11.63964 (+0.0000 seconds)
 10 02
Request: 01.06.2011 12:53:11.66964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:11.66964 (+0.0000 seconds)
 04 82 80 0C 14 03 B0 01 03 10 03 BC
Request: 01.06.2011 12:53:11.67964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:11.68964 (+0.0100 seconds)
 02
Request: 01.06.2011 12:53:11.68964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:11.68964 (+0.0000 seconds)
 04 82 80 0C 14 03 F1 03 32 03 00 00 00 02 00 02
 00 06 00 00 04 01 FF 04 00 10 10 [COLOR=Red][B]01[/B][/COLOR] [COLOR=Red][COLOR=DarkOrange][B]80[/B][/COLOR][COLOR=Black] 10[/COLOR][/COLOR]03 B4
Request: 01.06.2011 12:53:11.69964 (+0.0000 seconds)
 10 02
Answer: 01.06.2011 12:53:11.70964 (+0.0100 seconds)
 10
Request: 01.06.2011 12:53:11.70964 (+0.0000 seconds)
 04 82 80 0C 03 14 B0 01 03 10 03 BC
Answer: 01.06.2011 12:53:11.70964 (+0.0000 seconds)
 10
```
*Hier muss ich jedes Byte einzeln interpretieren, da ich 2x ein Byte einlese!
Ein Byte gehört EB0 und ein Byte EB1, also tauschen!*

-

_*DB11.DBW2:*_

```
Request: 01.06.2011 12:53:47.19264 (+36.4825 seconds)
 02
Answer: 01.06.2011 12:53:47.19264 (+0.0000 seconds)
 10
Request: 01.06.2011 12:53:47.20264 (+0.0100 seconds)
 04 82 80 0C 03 14 F1 04 32 01 00 00 00 03 00 0E
 00 00 04 01 12 0A 10 10 02 00 02 00 0B 84 00 00
 10 10 10 03 57
Answer: 01.06.2011 12:53:47.21264 (+0.0100 seconds)
 10 02
Request: 01.06.2011 12:53:47.23264 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:47.23264 (+0.0000 seconds)
 04 82 80 0C 14 03 B0 01 04 10 03 BB
Request: 01.06.2011 12:53:47.25264 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:47.25264 (+0.0000 seconds)
 02
Request: 01.06.2011 12:53:47.25264 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:53:47.25264 (+0.0000 seconds)
 04 82 80 0C 14 03 F1 04 32 03 00 00 00 03 00 02
 00 06 00 00 04 01 FF 04 00 10 10 [COLOR=Red][B]80 00[/B][/COLOR] 10 03 B3
Request: 01.06.2011 12:53:47.27264 (+0.0000 seconds)
 10 02
Answer: 01.06.2011 12:53:47.27264 (+0.0000 seconds)
 10
Request: 01.06.2011 12:53:47.27264 (+0.0000 seconds)
 04 82 80 0C 03 14 B0 01 04 10 03 BB
Answer: 01.06.2011 12:53:47.28264 (+0.0100 seconds)
 10
```
*Ich erware die dezimale Zahl -32768.
0x8000 = 0b1000000000000000 -> passt, hier darf nicht getauscht werden!*

-

_*DB11.DBD4:*_

```
Request: 01.06.2011 12:54:06.88964 (+18.6068 seconds)
 02
Answer: 01.06.2011 12:54:06.88964 (+0.0000 seconds)
 10
Request: 01.06.2011 12:54:06.89964 (+0.0100 seconds)
 04 82 80 0C 03 14 F1 05 32 01 00 00 00 04 00 0E
 00 00 04 01 12 0A 10 10 02 00 04 00 0B 84 00 00
 20 10 03 77
Answer: 01.06.2011 12:54:06.90964 (+0.0100 seconds)
 10 02
Request: 01.06.2011 12:54:06.92964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:54:06.93964 (+0.0100 seconds)
 04 82 80 0C 14 03 B0 01 05 10 03 BA
Request: 01.06.2011 12:54:06.94964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:54:06.94964 (+0.0000 seconds)
 02
Request: 01.06.2011 12:54:06.94964 (+0.0000 seconds)
 10
Answer: 01.06.2011 12:54:06.95964 (+0.0100 seconds)
 04 82 80 0C 14 03 F1 05 32 03 00 00 00 04 00 02
 00 08 00 00 04 01 FF 04 00 20 [B][COLOR=Red]07 5B CD 15[/COLOR][/B] 10 03
 9F
Request: 01.06.2011 12:54:06.96964 (+0.0000 seconds)
 10 02
Answer: 01.06.2011 12:54:06.96964 (+0.0000 seconds)
 10
Request: 01.06.2011 12:54:06.96964 (+0.0000 seconds)
 04 82 80 0C 03 14 B0 01 05 10 03 BA
Answer: 01.06.2011 12:54:06.97964 (+0.0100 seconds)
 10
```
*Ich erwarte den Wert 123456789 -> passt.*
*Auch hier darf ich nicht tauschen!*

-

Wie ich das auf dem Mikrocontroller (als Fake-Code) berechne:

```
[COLOR=SeaGreen][B]// EB0:[/B][/COLOR]
wert = byte[0];[COLOR=SeaGreen]  // [COLOR=Red][COLOR=SeaGreen]0x[/COLOR]01[/COLOR][/COLOR]

[COLOR=SeaGreen][B]// EB1:[/B][/COLOR]
wert = byte[1];[COLOR=SeaGreen]  // 0x[COLOR=DarkOrange]80[/COLOR][/COLOR]

[COLOR=SeaGreen][B]// EW0:[/B][/COLOR]
wert = (byte[1]<<8) + byte[0];  [COLOR=SeaGreen]// 0x[COLOR=DarkOrange]80[/COLOR]00 + 0x[COLOR=Red]01[/COLOR] = 0x[COLOR=DarkOrange]80[/COLOR][COLOR=Red]01[/COLOR][/COLOR]

usw...
```
Was mich jetzt nur wundert, ist, dass ich die Bytes (wenn ich das richtig verstanden habe) nur bei Eingängen oder Ausgängen drehen muss und als _x mal ein Byte_ interpretieren muss.
Denn ansonsten muss die Bytereihenfolge so bestehen bleiben.
*Denkfehler meinerseits?*

Ich habe leider keine analogen Ein- und Ausgänge, aber ich denke, dass ich wie bei EW oder AW (nicht PEW oder PAW) die Bytereihenfolge drehen muss. Kann jemand mal bitte den Datenverkehr wie ich aufzeichnen und hier posten?
Ich habe das Programm Free Serial Port Monitor von der Firma HHD benutzt.

Gruß,
poppycock


----------



## Zottel (1 Juni 2011)

Du hast mich gebeten, mir den Thread anzusehen. Ich "sehe" aber auch nicht auf einen Blick, wo dein Problem liegt.

Ein 8-Bit-Mikrocontroller hat erstmal gar keine "Endianness".
Du kannst Assembler-Programme schreiben, die die 16-Bit-Zahl 16394 als 
01000000 00001010 (big)
oder
00001010 01000000 (little)
im Speicher ablegen.

Die 32-Bit-Zahl 16394 ist dann: 
00000000 00000000 01000000 00001010 (big)
oder
00001010 01000000 00000000 00000000 (little)

Wenn du eine höhere Programmiersprache verwendest, z.B. C, so legen der Compiler und die Laufzeitbiliothek fest, an welche Konvention sie sich halten.
und erhälst
Die S7 verwendet big endian und wenn es dein Compiler/bibliothek anders halten, mußt du Mehrbyte-Variablen konvertieren.

Wenn du in der S7 ausführst:
L +66
T MD 6
so werden MB6,7,8 und 9 verändert
und es steht
0 0 0 66 darin.
Du kannst das MW8 laden und erhälst gleichfalls 66. Das Laden von MW6 liefert hingegen 0.

In C sieht das so aus
int32 * zeiger;  // Ein Zeiger. Hier verwendet, um absolute //Speicheradressen zu erzwingen  
int16 * zeiger6;
int16 * zeiger8;
zeiger=6;  
// Der Zeiger zeigt jetzt auf Byte 6
zeiger6=6;  zeiger8=8;
*zeiger=66;
// Lade Speicherstelle 6 mit 66 
//Auf einem "little endian"-System steht nun
// 66 0 0 0 in Speicherstellen 6,7,8,9
//Auf einem "big endian"-System steht nun
// 0 0 0 66 in Speicherstellen 6,7,8,9
printf("%d %d\n",*zeiger6,*zeiger8 );
// druckt 66 0 auf dem LE-System
// druckt 0 66 auf dem BE-System

Libnodave transferiert eine Folge von Bytes vom Speicher der S7 zum Speicher des Rechners. Welche Byte-Sequenzen auf einem "little endian" Rechner zu tauschen sind, hängt davon ab, wie der S7-Speicher genutzt wird, d.h. wo Mehrbyte-Zahlen beginnen.

HTH


----------



## poppycock (1 Juni 2011)

Hallo Zottel!



Zottel schrieb:


> Du hast mich gebeten, mir den Thread anzusehen.


Ja, vielen Dank, dass du dir die Mühe gemacht hast! 



Zottel schrieb:


> Ich "sehe" aber auch nicht auf einen Blick, wo dein Problem liegt.


Mein Problem lieg in der Interpretation der (einzelnen) einzulesenden Bytes.



Zottel schrieb:


> Ein 8-Bit-Mikrocontroller hat erstmal gar keine "Endianness".


Ja, das stimmt! Ich betrachte die "wichtigen" Bytes einzeln, quasi jeweils als 8bit-Wert.



Zottel schrieb:


> Wenn du eine höhere Programmiersprache verwendest, z.B. C, so legen der Compiler und die Laufzeitbiliothek fest, an welche Konvention sie sich halten.


Ich verwende AVR-GCC und das AVR Studio von Atmel.
In der Regel sollte der AVR die shorts und longs etc. als little endian betrachten, jedenfalls zeigt es mir so das Display an und habe auch nichts anderes darüber gelesen.



Zottel schrieb:


> Libnodave transferiert eine Folge von Bytes vom Speicher der S7 zum Speicher des Rechners. Welche Byte-Sequenzen auf einem "little endian" Rechner zu tauschen sind, hängt davon ab, wie der S7-Speicher genutzt wird, d.h. wo Mehrbyte-Zahlen beginnen.


Ja, genau, und da liegt auch das Problem.

Ich lese jedes Byte einzeln ein und speichere das in einem char-Array.

Ich beziehe mich jetzt auf die Eingänge EB0 und EB1.
So wird es über die serielle Schnittstelle transportiert: ... 0x01 0x80 ...
EB0 hat den Wert 0x01 -> z.B. gespeichert in byte[26]
EB1 hat den Wert 0x80 -> z.B. gespeichert in byte[27]
Im EB0 ist E0.0 gesetzt: macht in dezimal: 1
Im EB1 ist E1.7 gesetzt: macht in dezimal: 128
Betrachte ich das EW0 dual: 0b0000000110000000 = 384 = falsch!
Ich muss rechnen: 2^16 + 2^0 = 65537, also die Bytes tauschen!
D.h. ich caste das char vom EB1 zum unsigned short und schiebe den Wert um 8 stellen nach links.
Den Wert vom EB0 addiere ich einfach dazu.
Als Ergebnis erhalte ich: 0b1000000000000001, und das ist richtig.
Somit habe ich quasi die Bytes getauscht!

Nun beziehe ich mich auf das DB11.DBW2:
DB11.DBW wird so über die serielle Schnittstelle transportiert: ... 0x80 0x00 ...
Also:
byte[26] = 0x80
byte[27] = 0x00
Binär ausgedrückt: 0b1000000000000000
Das ist schon das richtige Endergebnis, da ich -32768 erwarte.
Wenn ich aber so vorgehe wie beim EW0, dann wird 0x00 als unsigned short gecastet, um 8 Stellen nach links verschoben und 0x80 hinzuaddiert.
Als duales Ergebnis bekomme ich 0b0000000010000000, und das ist falsch!

In beiden Fällen lese ich ein WORD (Oder etwa nicht?) aus, muss aber jeweils die Bytes anders interpretieren!
Das "warum das so ist" verstehe ich nicht, sorry.
Kann ich generell davon ausgehen, dass EW und AW prinzipiell als _x mal ein Byte _zu interpretieren sind?

Gruß,
poppycock


----------



## Zottel (2 Juni 2011)

poppycock schrieb:


> Ich verwende AVR-GCC und das AVR Studio von Atmel.
> In der Regel sollte der AVR die shorts und longs etc. als little endian betrachten, jedenfalls zeigt es mir so das Display an und habe auch nichts anderes darüber gelesen.


Ok


poppycock schrieb:


> Ich beziehe mich jetzt auf die Eingänge EB0 und EB1.
> So wird es über die serielle Schnittstelle transportiert: ... 0x01 0x80 ...
> EB0 hat den Wert 0x01 -> z.B. gespeichert in byte[26]
> EB1 hat den Wert 0x80 -> z.B. gespeichert in byte[27]
> ...


So. Und hier beginnen die verschiedenen Interpretationsmöglichkeiten:
Ein BE-System interpretiert den 16-Bit-Wert als 256*EB0+EB1, also 384.
Ein LE-System interpretiert den 16-Bit-Wert als 256*EB1+EB0. Was das ist, hängt auch noch davon ab, ob das höchste Bit als Vorzeichen angesehen wird.


poppycock schrieb:


> Ich muss rechnen: 2^16 + 2^0 = 65537


Nein, nicht rechnen! Kann mir mal ein Mensch erklären, warum soviele Leute dazu Potenzrechnungen anstellen wollen?


poppycock schrieb:


> , also die Bytes tauschen!


Eben. "Die Bytes tauschen" ist viel einfacher (Geschwindigkeit) und liefert IMMER das richtige Ergebnis. Beim Potenzrechnen hängt es von der Zahlendarstellung ab: float a=2^31; float b=2^0; a=a+b; und schon fällt die Eins aus der 23-Bit-Mantisse einfach raus...


poppycock schrieb:


> D.h. ich caste das char vom EB1 zum unsigned short und schiebe den Wert um 8 stellen nach links.
> Den Wert vom EB0 addiere ich einfach dazu.


Hoffe, dein Compiler ist so schlau, den Spezialfall "8 Stellen schieben" durch "tausche Bytes" zu ersetzen! Sonst packt er dir da womöglich 8 mal
LSL r0
ROL r1
in den Maschinencode...
 Schau dir die Tauschfunktionen in Libnodave an. Die machen das mit unions aus Byte-Array und Zielformat.


poppycock schrieb:


> Also:
> byte[26] = 0x80
> byte[27] = 0x00
> Binär ausgedrückt: 0b1000000000000000
> Das ist schon das richtige Endergebnis, da ich -32768 erwarte.



Nee, auf einem LE-System ist der 16Bit-Wert ab Byte 26
256*[byte27]+[byte26], also 256*0+128 !!!!!!!!!!!!!!!!!!!!!


poppycock schrieb:


> Kann ich generell davon ausgehen, dass EW und AW prinzipiell als _x mal ein Byte _zu interpretieren sind?


Die Regeln sind für DW, MW, AW, EW absolut gleich. Eine andere Frage ist, ob es sinnvoll ist, EWs oder AWs als Zahlen zu interpretieren statt als unabhängige Bits. Das muß man aber auch schon beim S7-Programmieren beachten: Ob etwa L EW0, L 5, -I etwa eine sinnvolle Anweisungsfolge seien könnte, hängt von der Umgebung ab. Wenn an E0.0 bis E1.7 etwas angschlossen ist, das Binärzahlen über parallele Leitungen liefert, dann ja.

Nehmen wir an, es hängt tatsächlich so ein Ding an E0.0 bis E1.7. Nehmen wir an, dieses Ding sei eine Waage.

L EW0 liefert dir in der S7 eine Zahl, ein Bruttogewiicht, z.B. 666.
L EW0, L 5, -I, T DB11.DBW2 errechnet das Netto, weil da z.B. ein Behälter 5kg Tara hat.
Du hast nun: EW0=666,EB0=2,EB1=154, DB11.DBW2=661, DB11.DBB2=2, DB11.DBB3=149
Das kannst du bis hierher alles mit Step7 nachvollziehen.

Nun liest du 2 Bytes ab EB0 mit Libnodave: Aus deinen Beispielen scheint hervorzugehen, daß die Ergebnisbytes bei dir ab byte 26 gespeichert werden. In Byte 26 steht also 2 und in Byte 27 154.
Dein LE-System würde diese Bytes als (usigned)16-Bit-Wert als 256*154 +2 interpretieren. Du mußt die Bytes tauschen.
Nun liest du 2 Bytes ab DB11.DBB2 mit Libnodave. Wieder stehn die Ergebnisbytes ab byte 26. In Byte 26 steht also 2 und in Byte 27 149.
Dein LE-System würde diese Bytes als (usigned)16-Bit-Wert als 256*149 +2 interpretieren. Du mußt die Bytes tauschen.

Nun soll deine S7 noch die verarbeiteten Gewichte aufsummieren. Dazu diene der folgende Code:
L DB11.DBD4
L DB11.DBW2 
+D
T DB11.DBD4
Du willst auch die Summe in deinem Rechner haben und liest jetzt 6 Bytes ab DB11.DBB2 mit Libnodave. Wieder stehen die  Ergebnisbytes ab byte 26. Byte 26 und 27 haben denselben Inhalt wie im vorigen Beispiel.
Du mußt Bytes 26 und 27 tauschen.
Ab Byte 28 steht aber jetzt ein 32-Bit-Wert. Damit dein System diesen richtig interpretiet, mußt du so tauschen: 
Byte 28 mit Byte 31, Byte 29 mit Byte 30.

Nun ändere ich aus Bosheit den S7-Code wie folgt:
L DB11.DBD5
L DB11.DBW2 
+D
T DB11.DBD5
 Die Summe steht jetzt in DBD5. Das ist ungewöhnlich, aber nicht verboten. Es mag langsamer sein, als wenn die Adressen durch 2 oder 4 teilbar wären.
DBB4 ist einfach unbenutzt. Die Übertragung der 7 Bytes, die DBW2 und DBD5 enthalten, ist erheblich schneller als DBW2 und DBD5 nacheinander zu lesen.

Du liest also 7 Bytes ab DB11.DBB2 mit Libnodave. Wieder stehen die  Ergebnisbytes ab byte 26. Byte 26 und 27 haben denselben Inhalt wie in den vorigen 2 Beispielen.
Du mußt Bytes 26 und 27 tauschen.
In Byte 28 steht der jetzt sinnlose Inhalt von DBB4.
Ab Byte 29 steht der 32-Bit-Wert. Damit dein System diesen richtig interpretiert, mußt du so tauschen: 
Byte 29 mit Byte 32, Byte 30 mit Byte 31.

Aus diesen Beispielen sollst Du erkennen, daß man wissen muß, welche Datentypen an welcher Stelle im Speicher der S7 stehen, um zu entscheiden, welche Bytes zu tauschen sind.

Das Tauschen selbst kannst du den Funktionen daveGet<TypeName> oder daveGet<TypeName>At überlassen. Die machen das effizient. Oder hat AVRGcc damit Probleme? Spätestens bei Fließkommazahlen (S7:REAL, C:float) kommt die Fummelei mit 2^x, Schieben und AND und OR an ihre Grenzen (Ja, mir hat schon mal einer geschrieben, daß er REALs so bearbeitet...).


----------



## poppycock (2 Juni 2011)

Hallo Zottel und vielen Dank für deinen ausführlichen Text! 



Zottel schrieb:


> Und hier beginnen die verschiedenen Interpretationsmöglichkeiten:
> Ein BE-System interpretiert den 16-Bit-Wert als 256*EB0+EB1, also 384.
> Ein LE-System interpretiert den 16-Bit-Wert als 256*EB1+EB0. Was das ist, hängt auch noch davon ab, ob das höchste Bit als Vorzeichen angesehen wird.


Also kommt es dabei drauf an, was ich da interpretieren möchte?!
Ich muss hier wie für ein LE-System darauf reagieren und das EB1 um 8 Stellen nach links schieben. Ich betrachte die Bytes EINZELN!



Zottel schrieb:


> Nein, nicht rechnen! Kann mir mal ein Mensch erklären, warum soviele Leute dazu Potenzrechnungen anstellen wollen?


Um Gottes willen, ich rechne NICHT mit Potenzen, das sollte nur ein Beispiel sein, wie man von der binären Zahl die dezimale Zahl errechnet.
Ich schiebe die Bytes!



Zottel schrieb:


> "Die Bytes tauschen" ist viel einfacher (Geschwindigkeit) und liefert IMMER das richtige Ergebnis. Beim Potenzrechnen hängt es von der Zahlendarstellung ab: float a=2^31; float b=2^0; a=a+b; und schon fällt die Eins aus der 23-Bit-Mantisse einfach raus...


Darum werden auch die Bytes geschoben!



Zottel schrieb:


> Schau dir die Tauschfunktionen in Libnodave an. Die machen das mit unions aus Byte-Array und Zielformat.


Habe bisher weder unions noch structs verwendet. Da muss ich mich erstmal einlesen!
Aus deinem Code von sourceforge habe ich folgendes entnommen:

_*nodave.h / Revision 1.4:*_

```
static inline float daveGetFloat(daveConnection * dc) {
union {
 float a;
 int b;
 } f;
f.b=(bswap_32(* ( ((int*)( (int*)dc->dataPointer)++) )));
return (f.a);
} 

static inline float toPLCfloat(float ff) {
union {
 float a;
 int b;
 } f;
 f.a=ff;
 f.b=(bswap_32(f.b));
 return (f.a);
} 

static inline int daveGetInteger(daveConnection * dc){
 return (bswap_32(* (((int*)( (int*)dc->dataPointer)++) )));
}

static inline int daveGetDWORD(daveConnection * dc) {
 return (bswap_32(* ( ((int*)( (int*)dc->dataPointer)++) )));
}

static inline unsigned int daveGetUnsignedInteger(daveConnection * dc) {
 return (bswap_32(* ( ((unsigned int*)( (unsigned int*)dc->dataPointer)++) )));
}

static inline unsigned int daveGetWORD(daveConnection * dc) {
 return (bswap_16(* ( ((short*)( (short*)dc->dataPointer)++) ))); 
}
```
_*Und die test.c / Revision 1.3:*_

```
a=daveGetDWORD(dc);
b=daveGetDWORD(dc);
c=daveGetDWORD(dc);
d=daveGetFloat(dc);
e=daveGetFloat(dc);
f=daveGetFloat(dc);
```
Da ich mit einem 8bit-Controller arbeite, funktioniert das Byteswap ohne Anpassung nicht!
Allerdings muss ich mir nun genauer angucken, wie du das genau machst und evtl. für einen 8biter umschreiben.



Zottel schrieb:


> Nee, auf einem LE-System ist der 16Bit-Wert ab Byte 26 256*[byte27]+[byte26], also 256*0+128 !!!!!!!!!!!!!!!!!!!!!


Hhm, wenn ich das so rechne, kommt ein falsches Ergebnis raus!
byte[26] = 0x80
byte[27] = 0x00
Binär ausgedrückt: 0b1000000000000000
Nun rechne ich 256*[byte27]+[byte26] = 0x0080 = 128 dezimal.
Das ist das Ergebnis, das auch du bekommst.
Aber ich erwarte doch den Wert -32768.
Wie komme ich nun von 128 auf -32768?
Das ist ja gerade das, was mich verwundert.



Zottel schrieb:


> Die Regeln sind für DW, MW, AW, EW absolut gleich. Eine andere Frage ist, ob es sinnvoll ist, EWs oder AWs als Zahlen zu interpretieren statt als unabhängige Bits.


Ich könnte EWs oder AWs byteweise betrachten!
Somit entfällt das "kuriose" Schieben.
Als Ergebnis _könnte_ ich folgendes auf dem Display bei EWs und AWs beispielsweise anzeigen:

```
EB0 = 00101101
EB1 = 10110010
EB2 = 11010111
EB3 = 10000100
```
Also quasi:

```
utoa(byte[26],buff26,2) für EB0
utoa(byte[27],buff27,2) für EB1
utoa(byte[28],buff28,2) für EB2
utoa(byte[29],buff29,2) für EB3
```



Zottel schrieb:


> Nun liest du 2 Bytes ab EB0 mit Libnodave: Aus deinen Beispielen scheint hervorzugehen, daß die Ergebnisbytes bei dir ab byte 26 gespeichert werden. In Byte 26 steht also 2 und in Byte 27 154.
> Dein LE-System würde diese Bytes als (usigned)16-Bit-Wert als 256*154 +2 interpretieren. Du mußt die Bytes tauschen.


Ich habe eine andere _ReadBytes-Funktion geschrieben, die das DLE-Doubling so behandelt, dass ALLE nötigen Datenbytes, die eingelesen werden, ab Byte 26 bis Byte 29 stehen!
Mehr als ein Doppelwort will ich sowieso nicht auf einmal auslesen!
Richtig ist, dass ich dort die Bytes tauschen muss, wenn ich einen dezimalen Zahlenwert haben möchte.



Zottel schrieb:


> Nun liest du 2 Bytes ab DB11.DBB2 mit Libnodave. Wieder stehn die Ergebnisbytes ab byte 26. In Byte 26 steht also 2 und in Byte 27 149.
> Dein LE-System würde diese Bytes als (usigned)16-Bit-Wert als 256*149 +2 interpretieren. Du mußt die Bytes tauschen.


Okay...
[byte26] = 2 = 0x02
[byte27] = 149 = 0x95
0x9502 = 38146 dezimal = falsch?!
Nach dem Tausch: 0x0295 = 661 dezimal = richtig?!



Zottel schrieb:


> Aus diesen Beispielen sollst Du erkennen, daß man wissen muß, welche Datentypen an welcher Stelle im Speicher der S7 stehen, um zu entscheiden, welche Bytes zu tauschen sind.


Also kommt es nur darauf an, wie man die Daten interpretiert bzw. man wissen muss, welchen Wert man zu erwarten hat...



Zottel schrieb:


> Das Tauschen selbst kannst du den Funktionen daveGet<TypeName> oder daveGet<TypeName>At überlassen. Die machen das effizient. Oder hat AVRGcc damit Probleme? Spätestens bei Fließkommazahlen (S7:REAL, C:float) kommt die Fummelei mit 2^x, Schieben und AND und OR an ihre Grenzen (Ja, mir hat schon mal einer geschrieben, daß er REALs so bearbeitet...).


Um float habe ich mich noch nicht gekümmert, kommt demnächst.
Und ich verspreche dir, dass ich nicht mit Potenzen rechne! 

Ich hoffe, ich habe das jetzt um diese Uhrzeit noch einigermaßen verstanden und nichts verbockt...

Vielen Dank,
poppycock


----------



## Zottel (3 Juni 2011)

Habe nicht erforscht, ob AVRgcc die Headerdatei bswap.h kennt.
Hier mal eine Nachbildung dieser Funktionen:

static inline int16 bswap_16(int16 ff) { 

union {  // Ne union, das sind mehrere Daten verschiedenen Typs, die
// denselben Speicherplatz belegen.  Man könnte auch sagen: Verschiedene
// Sichtweisen auf diesen Speicherplatz, verschiedene Interpretationen.
  unsigned int16 a;     // eine 16-Bit-Variable 
  unsigned char b[2];   // 2 Bytes, an DERSELBEN Position im Speicher
 } f;
char xc;
 f.a=ff;
 xc=f.b[0];
 f.b[0]=f.b[1];
  f.b[1]=xc;
 return (f.a); } 

static inline int32 bswap_32(int32 ff) { 

union {  // Ne union, das sind mehrere Daten verschiedenen Typs, die
// denselben Speicherplatz belegen.  Man könnte auch sagen: Verschiedene
// Sichtweisen auf diesen Speicherplatz, verschiedene Interpretationen.
  unsigned int32 a;     // eine 32-Bit-Variable 
  unsigned char b[4];   // 4 Bytes, an DERSELBEN Position im Speicher
 } f;
char xc;
 f.a=ff;
 xc=f.b[0];
 f.b[0]=f.b[3];
  f.b[3]=xc;

 xc=f.b[1];
 f.b[1]=f.b[2];
  f.b[2]=xc;

 return (f.a); }


----------



## Zottel (3 Juni 2011)

poppycock schrieb:


> Also kommt es nur darauf an, wie man die Daten interpretiert bzw. man wissen muss, welchen Wert man zu erwarten hat...


Es geht nicht darum, welchen Wert man erwartet. Im beispiel der Waage ist jedes Gewicht zwischen 0 und 65536 Kg verarbeitbar, zwischen 5 und 65536 Kg sinnvoll.
Es geht darum, welchen Datentyp man an erwartet. Es ist eine Art von Vereinbarung zwischen dem S7-Programm und der Libnodave-Anwendung, daß an einer bestimmten Stelle des Speicher(abbilde)s eine Gruppe von Bytes beginnt, die eine 16-Bit, 32-Bit oder Real-Zahl darstellt.


----------



## poppycock (3 Juni 2011)

Hallo Zottel!



Zottel schrieb:


> Es geht nicht darum, welchen Wert man erwartet. [...] Es geht darum, welchen Datentyp man an erwartet. Es ist eine Art von Vereinbarung zwischen dem S7-Programm und der Libnodave-Anwendung, daß an einer bestimmten Stelle des Speicher(abbilde)s eine Gruppe von Bytes beginnt, die eine 16-Bit, 32-Bit oder Real-Zahl darstellt.


Ich sollte die Endianess-Sache ganz anders betrachten.
Und ja, das stimmt, es kommt darauf an, welchen Datentyp ich erwarte!
Jetzt habe ich (so glaube ich) die Sache besser verstanden.

Vielen Dank für das union-Beispiel!
So eine Funktion ist echt genial, kannte ich vorher nicht!
Ich kann mit einer union wunderbar Datenbytes so interpretieren, wie ich diese brauche! Danke dafür! :-D

Und zum Schluss:


Zottel schrieb:


> Ein BE-System interpretiert den 16-Bit-Wert als 256*EB0+EB1, also 384.


Ja, das stimmt. Im Anhang ist das EW0 zu sehen, wie es hier besprochen wurde!

Gruß und besten Dank,
poppycock


----------

