# Abfrage diverser Pheripherieadressen



## eYe (28 Januar 2011)

Nabend,

jedesmal ärgere ich mich wenn ich eine höhere Anzahl an Profibusteilnehmern habe, dass ich die Adressen mühselig von Hand ändern muss und denke mir die Jungs im Forum machen das mit Sicherheit geschickter...


```
L     PEB  256
      T     DB11.DBB    0
      L     PEB  257
      T     DB11.DBB    1
      L     PED  258
      T     DB11.DBD    2
      L     PED  262
      T     DB11.DBD    6
      L     PED  266
      T     DB11.DBD   10
      L     PED  270
      T     DB11.DBD   14
      L     PED  274
      T     DB11.DBD   18
      L     PED  278
      T     DB11.DBD   22
      L     PED  282
      T     DB11.DBD   26
      L     PED  286
      T     DB11.DBD   30
      L     PED  290
      T     DB11.DBD   34
      L     PED  294
      T     DB11.DBD   38
      L     PED  298
      T     DB11.DBD   42
      L     PED  302
      T     DB11.DBD   46
      L     PED  306
      T     DB11.DBD   50
      L     PED  310
      T     DB11.DBD   54
      L     PED  314
      T     DB11.DBD   58
      L     PED  318
      T     DB11.DBD   62
      L     PED  322
      T     DB11.DBD   66
      L     PED  326
      T     DB11.DBD   70
      L     PED  330
      T     DB11.DBD   74
      L     PED  334
      T     DB11.DBD   78
      L     PED  338
      T     DB11.DBD   82
      L     PED  342
      T     DB11.DBD   86
      L     PED  346
      T     DB11.DBD   90
      L     PED  350
      T     DB11.DBD   94
      L     PED  358
      T     DB11.DBD   98
      L     PED  362
      T     DB11.DBD  102
      L     PED  366
      T     DB11.DBD  106
      L     PED  370
      T     DB11.DBD  110
      L     PED  374
      T     DB11.DBD  114
```

Der oben geschrieben "Code" ist zum auslesen diverser Messwerte aus einem Profibusteilnehmer. Nun gibt es von dieser Art Teilnehmer insgesammt 8 Stück und ich würde gerne wissen, ob es eine einfache Möglichkeit gibt diese Werte schnell in die dazugehörigen DBs zu befördern. Momentan passe ich für jeden Teilnehmer die Adressen von Hand an, wobei ich bei den DB Adressen schon so schlau bin und mit "Suchen & Ersetzen" arbeite.
Aber bei den Pheripherieadressen geht das leider nicht 

Wie löst ihr sowas, kann ich nicht irgendwie "Startbyte & Anzahl" vorgeben und dann den entsprechenden DB und es wird automatisch reinkopiert?

*verzweifel*


----------



## Thomas_v2.1 (29 Januar 2011)

Hi,
gerade für Profibusteilnehmer und wenn man dort Daten konsistent lesen muss gibt es den Baustein "DPRD_DAT", bzw. SFC14, zu finden in der Standard Library unter "System Function Blocks".

Ansonsten hindert dich keiner daran dir einen eigenen Baustein zu schreiben, der in einer Schleife Schleife die Kopierfunktion durchführt. Als Parameter bietet sich dabei der Datentyp ANY an.
Wenn die Adressbereiche deiner Profibus-Slaves im Prozessabbild liegen, kannst du auch den SFC20 "BLKMOV" einsetzen (dann aber mit P#E.. als Bereichskennung).


----------



## eYe (31 Januar 2011)

Hallo Thomas,

"BLKMOV" kommt nicht in Frage da die Daten in der Regel außerhalb des Prozessabbildes liegen. Der SFC14 hört sich interessant an, allerdings finde ich es etwas nervig, dass man für jedes eingebundene Modul des DP-Teilnehmers einen eigenen Aufruf starten muss.
Was also bleibt wäre eine selbst geschrieben Funktion. Sicherlich hält mich momentan kein Mensch davon ab so eine zu schreiben, aber leider doch meine Unwissenheit 

Aber nur mal zum Verständis ein paar Fragen:

1) Es würde reichen einen FC zu schreiben, da ich ja keine Daten bei dem Kopiervorgang über einen Zyklus zwischenspeichern muss?
2) Als Eingänge lege ich die Startadresse(ADR) als ANY und die Anzahl(COUNT) der Bytes fest? (Ist wohl das beste mit Bytes zu arbeiten, wenn man diverse unterschiedliche Datentypen hat?)
3) Als Ausgang muss die Startadresse des Speicherplatzes(DATA) der Daten festgelegt werden, also auch ein ANY?
4) In der Function werden nun über eine FOR Schleife "einfach" die einzelnen Bytes kopiert. PEW -> DB
Leider hapert es hier extrem bei mir, wie zähle ich in jedem Schleifendurchgang die Eingangs und Ausgangsadresse hoch?


```
FOR INDEX: 1 TO COUNT DO
DATA:=ADR;
[COLOR=Red]ADR:=ADR+1;
DATA:DATA+1;[/COLOR]
END_FOR;
```

5) Empfiehlst du das ganze mit SCL zu lösen, oder wäre auch eine Lösung in AWL übersichtlich machbar?


----------



## Paule (31 Januar 2011)

Auch wenn die Frage an Thomas gerichtet war, probiere ich das mal zu beantworten. (Thomas kann mich dann wieder verbessern.  )
Also in deinem Fall ist INDEX deine Laufvariabel und COUNT die Anzahl wie oft du kopieren willst.

```
FOR INDEX :=  1 TO COUNT DO
   DATA := ADR;
[COLOR=black]   ADR  := ADR[INDEX];[/COLOR]
[COLOR=black]   DATA := DATA[INDEX];[/COLOR]
END_FOR;
```


----------



## eYe (31 Januar 2011)

Hab mir jetzt mal noch ein wenig mehr Halbwisen angelesen, einige Fragen haben sich erledigt aber dafür sind einige neue dazu gekommen. 

Im Grunde genommen bin ich mir momentan gar nicht mehr so sicher ob es eine wirkliche Lösung für dieses Problem gibt. Alle anderen Posts die ich gelesen habe zu diesem thema, haben im Endeffekt SFC14,15 oder 20 verwendet...

Hier mal eine Grundlage zum Diskutieren:

```
auf: Nop 0 //Schleifenanfang
L #Startadresse
SLD 3
LAR 1
L PEB [AR1, P#0.0] //Erzeugung Pointer der Eingangsadresse

[COLOR=Black]AUF DB [#t_DB] [/COLOR]//Öffnen des DBs
L #Data
SLD 3
LAR1
T DBB [AR1,P#0.0] //Erzeugung Pointer der Speicheradresse

L#Startadresse
L 1
+I
T #Startadresse //Inkrementieren der Startadresse

L#Data
L 1
+I
T #Data //Inkrementieren der Speicheradresse

L #Count //Schleifenzähler/Anzahl der Bytes
L 1
-I //Dekrementieren des Schleifenzählers
L 0
==I //Wenn Alle Bytes kopiert beenden
SPB ende
SPA anf
ende: Nop 0
```
Aktuelle Fragen:

1) Ist das kopieren mittels einer Funktion von einer beliebigen Startadresse zu einer beliebigen Speicheradresse mit beliebig vielen Bytes überhaupt möglich?
2) Wenn ja, geht mein Ansatz in die richtige Richtung?
3) Wie kann ich den Aufruf des DBs variabel gestalten?

Danke,
eYe


----------



## Bär1971 (31 Januar 2011)

Hy,

du legst in deinem FC eine Eingangsvariable (z.Bsp DB_NR) als INTEGER an.
In deinem Baustein kopierst du die Eingangsvariable in eine TEMP-Variable (z.Bsp. t_DB_NR) als INTEGER. 

L DB_NR
T t_DB_NR

(Ich weis nicht warum, aber das ganze funktioniert nur mit einer Temp-Variablen.)
Dann änderst du deinen DB-Aufruf wie folgt:

AUF DB [t_DB_NR]



Genauso kannst du Eingangsvariablen benutzen um die Anzahl der Durchläufe einer Schleife zu bestimmen und Eingangsvariablen zum bilden eines Pointers der innerhalb der Schleife hochgezählt wird als indirekte Adressierung. Hilfe zur Umsetzung findest du unter den Stichworten Datenbank in der Suche und in den FAQ die Themen "Pointer Zeiger FIFO LIFO" und "Any-Zeiger für Datentypen".

Verzeih mir, das ich es dir grad aus dem Stehgreif nicht genau sagen kann wie du deine Anwendung programmierst. Ich hab selbst erst angefangen mit den Zeigern zu spielen und müsste im Geschäft in meinem Programm nachsehen. Aber mit den Hilfen hier hab ich mich auch einarbeiten können und eine flexible Datenbank aufgebaut. Die benötigten Funktionen sind die Selben wie du brauchst für das was du machen magst.


----------



## Thomas_v2.1 (31 Januar 2011)

Um die Funktion universell zu gestalten würde ich als Zielbereich auch alle Datenbereiche (M/DB/E/A) zulassen.
Als Quellbereich reicht aus, diesen Baustein nur auf Peripherieeingänge zu spezifizieren, da man eben für alle anderen Bereiche gleich den Blockmove verwenden kann.

Ich würde das Ganze wie folgtprogrammieren, wobei hier noch Optimierunspotential bezüglich der Zugriffsbreite PEx besteht. Peripheriezugriffe (zumindest auf die CPU internen) sind schon sehr langsam. Mit einem Word-Zugriff ließe sich das ganze etwas beschleunigen (PEB typisch 13,7 µs; PEW typisch 17,4 µs; PED typisch 31,3 µs). Der Doppelwordzugriff bringt also verhältnismäßig nicht mehr viel.

In meinem Beispiel habe ich beide indirekten Zugriffsarten verwendet (speicher- und registerindirekt).


```
FUNCTION "PEW_CPY" : VOID

VAR_INPUT
  START_PEW : INT ;	//Start PEW Nummer
  COUNT : INT ;	//Anzahl an Bytes
END_VAR
VAR_IN_OUT
  DST : ANY ;	//Zieladresse, Datentyp Byte erlaubt
END_VAR
VAR_TEMP
  dbnum : INT ;	
  p_adr : DWORD ;	
  i : INT ;	
  area : DWORD ;	
END_VAR
BEGIN
NETWORK
TITLE =

      SET   ; 
      SAVE  ; 

      L     P##DST; 
      LAR1  ; 

// Prüfen ob Typangabe in Any = Byte
      L     B [AR1,P#1.0]; 
      L     2; // 2 = Datenkennung Byte
      <>I   ; 
      SPB   fehl; 

// Prüfen ob Längenangaben gleich sind
      L     W [AR1,P#2.0]; 
      L     #COUNT; 
      <>I   ; 
      SPB   fehl; 

// DB Nummer aus Any
      L     W [AR1,P#4.0]; 
      T     #dbnum; 

// Datenbaustein kann auf jeden Fall geöffnet werden, 
// bei anderen Bereichskennungen als DB steht hier 0.
// Ein AUF DB 0 wird ohne Fehler abgearbeitet.
      AUF   DB [#dbnum]; 

// Quell Pointer bilden
      L     #START_PEW; 
      SLD   3; 
      T     #p_adr; 
// Ziel Pointer aus Any holen
      L     D [AR1,P#6.0]; 
      LAR2  ; 
// Bereichskennung aus Pointer für später merken
      UD    DW#16#FF000000; 
      T     #area; 

      L     #COUNT; 
next: T     #i; 

      L     PEB [#p_adr]; // Zugriff speicherindirekt
      T     B [AR2,P#0.0]; // Zugriff registerindirekt, bereichsübergreifend

// PEW Zeiger erhöhen
      L     #p_adr; 
      L     P#1.0; 
      +D    ; 
      T     #p_adr; 
// Any Zeiger erhöhen
      TAR2  ; 
      UD    DW#16#FFFFFF; // Bereichskennung für Addition ausmaskieren
      L     P#1.0; 
      +D    ; 
      L     #area; // und Bereichskennung wieder einmaskieren
      OD    ; 
      LAR2  ; 

      L     #i; 
      LOOP  next; 

      SPA   ok; 

fehl: CLR   ; 
      SAVE  ; 
ok:   BE    ; 

END_FUNCTION
```


----------



## Jochen Kühner (31 Januar 2011)

Thomas_v2.1 schrieb:


> Hi,
> gerade für Profibusteilnehmer und wenn man dort Daten konsistent lesen muss gibt es den Baustein "DPRD_DAT", bzw. SFC14, zu finden in der Standard Library unter "System Function Blocks".



Es geht mit DPRD_DAT aber auch nur wenn der DP Teilnehmer das konsistente lesen von soviel Bytes auch unterstützt!

D.h. wenn Ich 20 Bytes lesen will und der Slave kann nur 10 Bytes konsistent, dann kann Ich nicht mit einem DPRD_DAT Aufruf auf einmal 20 Bytes lesen!


----------



## Jochen Kühner (31 Januar 2011)

*Mhmmm*

@Thomas:


```
// Any Zeiger erhöhen
      TAR2  ; 
      UD    DW#16#FFFFFF; // Bereichskennung für Addition ausmaskieren
      L     P#1.0; 
      +D    ; 
      L     #area; // und Bereichskennung wieder einmaskieren
      OD    ; 
      LAR2  ;
```

Warum machst du hier nicht einfach ein

```
+AR2  P#1.0
```


----------



## eYe (1 Februar 2011)

Hallo Thomas,

zunächst einmal vielen Dank das du dir die Zeit genommen hast für diese ausführliche Antwort. 
Ich habe deinen code soweit nachvollziehen können und wenn ich morgen dazu komme, werde ich ihn gleich mal testen.

Das Ausmaskieren der Bereichskennung habe ich nicht ganz nachvollziehen können.

```
// Any Zeiger erhöhen
      TAR2  ; 
      UD    DW#16#FFFFFF; // Bereichskennung für Addition ausmaskieren
      L     P#1.0; 
      +D    ; 
      L     #area; // und Bereichskennung wieder einmaskieren
      OD    ; 
      LAR2  ;
```


```
Bit    |15..         ..8 | 7..         ..0 |
Byte 0 |   10h für S7    |    [COLOR=skyblue]Datentyp[/COLOR]     | Byte 1
Byte 2 |        [COLOR=purple]Wiederholungsfaktor[/COLOR]        | Byte 3
Byte 4 |         [COLOR=deeppink]DB-Nummer (oder 0)[/COLOR]        | Byte 5
Byte 6 | [COLOR=royalblue]Speicherbereich[/COLOR] | 0 0 0 0 0 [COLOR=orange]B B B[/COLOR] | Byte 7
Byte 8 | [COLOR=orange]B B B B B B B B[/COLOR] | [COLOR=orange]B B B B B[/COLOR] [COLOR=orangered]b b b[/COLOR] | Byte 9

[SIZE=1][B]Quelle: [URL]http://www.sps-forum.de/showthread.php?t=12923[/URL][/B][/SIZE]
```
1) Warum muss man das so machen, geht es nicht auch wie Jochen Kühner vorschlägt?
2) Der Anypointer besteht ja aus 80Bit, wenn ich diesen nun in den AKKU1 lade(32bit) bleiben also nur noch Byte6-9 übrig?


----------



## Jochen Kühner (1 Februar 2011)

eYe schrieb:


> 1) Warum muss man das so machen, geht es nicht auch wie Jochen Kühner vorschlägt?


Das hab Ich mich ja auch gefragt.



eYe schrieb:


> 2) Der Anypointer besteht ja aus 80Bit, wenn ich diesen nun in den AKKU1 lade(32bit) bleiben also nur noch Byte6-9 übrig?


Er lädt ja nicht den Any in den Akku, sondern das AR, und im AR ist kein Any sondern ein Pointer! (wobei es nicht dem Step7 Parametertyp Pointer entspricht, da dieser auch noch den DB enthält). Und ein Pointer ist nur 4 Byte Groß!


----------



## Thomas_v2.1 (1 Februar 2011)

Jochen Kühner schrieb:


> Das hab Ich mich ja auch gefragt.



+AR1 arbeitet nur für Werte bis 32767.
Auch wenn es selten vorkommt dass man größere DBs hat, so funktioniert meine Lösung auch für Adressen > 32767.


----------



## Jochen Kühner (1 Februar 2011)

Aber zum addieren müsstest du doch die Bereichskennung nicht ausblenden!


----------



## Jochen Kühner (1 Februar 2011)

Thomas_v2.1 schrieb:


> +AR1 arbeitet nur für Werte bis 32767.
> Auch wenn es selten vorkommt dass man größere DBs hat, so funktioniert meine Lösung auch für Adressen > 32767.



Laut Hilfe kann +AR2 nur Zahlen bis +32767 addieren, d.h. aber ja nicht das AR2 nur bis +32767 geht! Und da du ja in dem Baustein immer nur P#1.0 addierst sollte es ja auch mit


```
+AR2  P#1.0
```

richtig sein!


----------



## Jochen Kühner (1 Februar 2011)

Noch ein Auszug aus der Hilfe:


> +AR2 (Addiere zu AR2) addiert einen Versatz, der entweder in der Anweisung oder in AKKU1-L angegeben wird, zum Inhalt von AR2. Die Ganzzahl (16 Bit) wird zunächst vorzeichenrichtig auf 24 Bit erweitert und danach zu den niederwertigsten 24 Bit von AR 2 (Teil der relativen Adresse in AR2) addiert. Der Teil der Bereichskennung in AR2 (Bits 24, 25 und 26) wird nicht verändert. Die Operation wird ausgeführt, ohne die Statusbits zu berücksichtigen oder zu beeinflussen.



Da steht ja "wird zunächst vorzeichenrichtig auf 24 Bit erweitert" sollte also auch mit größeren Adressen gehen!


----------



## eYe (1 Februar 2011)

Nabend,

wollte nur bescheid sagen das die Funktion einwandfrei funktioniert 
Sowohl mit Ausmaskierung der Bereichskennung, wie auch mit "+AR2 P#1.0".

Also nochmal besten Dank an Thomas!

:TOOL:


----------



## Thomas_v2.1 (1 Februar 2011)

Jochen Kühner schrieb:


> Laut Hilfe kann +AR2 nur Zahlen bis +32767 addieren, d.h. aber ja nicht das AR2 nur bis +32767 geht! Und da du ja in dem Baustein immer nur P#1.0 addierst sollte es ja auch mit
> 
> 
> ```
> ...



Da hast du Recht Jochen.
Die Begrenzung von +ARx tritt erst auf wenn man damit größere Werte als 32767 (oder P#4095.7) addieren will, ist hier also überflüssig.


----------



## Jochen Kühner (2 Februar 2011)

Thomas_v2.1 schrieb:


> Da hast du Recht Jochen.
> Die Begrenzung von +ARx tritt erst auf wenn man damit größere Werte als 32767 (oder P#4095.7) addieren will, ist hier also überflüssig.



Macht ja Siemens bei Zugriffen auf höherwertige statische Daten im FB auch so. Dort wird +AR2 Pxx sooft aufgerufen bis die gewünschte Adresse erreicht ist!


----------

