TIA 2 x 1500er BSend, BRcv

Zuviel Werbung?
-> Hier kostenlos registrieren
Meinst du im Bild Zwischenablage 2 ?
Das sollte eigentlich OK sein da die eine ID 102 zur CPU 111.200 und die andere ID 102 zur CPU 84.220 gehört.

Hab jetzt mal eben aber einer der beiden Verbindungen eine andere ID gegeben. Geht nicht.
 
irgendwie komm ich zwar nicht ganz mit,

aber was hast Du am LEN dran und was am RD_1 bzw. SD_1 ?

an den RD_1 bzw. SD_1 sieh das bei mir normalerweise so aus: P#DB300.DBX0.0 BYTE 60
am LEN steht dann dementsprechend: 60 dez

In Deinen Scrennshots hast Du an den LEN immer 0 angetragen?

Gruß.

PS: ok in der Hilfe steht
LEN = 0 hab ich aber noch nie ausprobiert...

trage ich bei LEN 1 ein gehts trotzdem nicht.

RD_1 und SD_1 sind einfach ein Taktmerkerbyte. TIA macht da den Namen draus.
Vom Prinzip wäre das p#DB10.dbx0.0 byte 1 und p#DB12.dbx0.0 byte 1

Bei BRcv sollte ja bei länge die Anzahl der empfangenen bytes stehen.

Der einzige unterschied zwischen funktioniert (spezifizierte Verbindung. Beide CPU im gleichen Projekt und TIA vergibt die IDs automatisch) und funktioniert nicht (unspezifizierte Verbindung) ist hier scheinbar das Problem.
Ich möchte nicht wirklich in der übergeordneten CPU Unmegen an Dummy-CPU's anlegen
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Mit LEN = 0 sollte den gesammte Datenstruktur auf SD_1 übertragen werden.
Auf der 88-220 steht bei STATUS = 11hex = 17dec = "Warning: Instruction receives data asynchronously. The LEN parameter shows the amount of data already received in bytes." aber bei LEN immer noch 0.

Ist "put_i7sz".takt ein INT ?
Als Versuch probier mal mit eine grössere Struktur.
Und dann mit LEN=0, und mit LEN=eine spezifizierte Menge.

edit: Ignorier mein Beitrag. Wenn das Problem ist unspezifiziert vs. spezifiziert hat es nicht damit zu tun.
 
Zuletzt bearbeitet:
Ich denke wir machen es jetzt mal auf die schnelle tour.
Ich hab da schon länger mal n paar Bausteine gebastelt wie ich das mache. Ist verbesserungswürdig und ich hab das einfach gemacht aus reinem Interesse. Die funktionieren grundsätzlich.
Bei hinweisen was ich falsch gemacht habe oder was dämlich ist. Nur zu ich lerne gerne.

Erstmal der Baustein der in der 300/400er zum Zuge kommt.
Code:
FUNCTION_BLOCK MasterCom

{ S7_Optimized_Access := 'FALSE' }
// Dieser Baustein regelt die Kommunikation zu einer Partnerstation Slave
// Es ist pro S7 Verbindung nur ein MasterCom zulässig da die Koppel_ID im Baustein vergeben wird
// Da BSend und BRCV verwendet werden sind nur S7 Verbindungen erlaubt
// Nach möglichkeit sind die SFB12/13 BSEND/BRCV zu verwenden, wenn nicht durch die CPU unterstützt
// dann die FB12/13 aus der Kommunikationsbibliothek Step7 in die CPU laden. 
// SFB und FB arbeiten zusammen
//
// V1.0 Startversion
// V1.1 Umgebaut auf ANY_Pointer
// V1.2 ANY in Baustein Integriert (Structur gebildet)
// V1.10 Baustein erkennt jetzt selbstständig ob Ein DB, Pointer oder Structur angehängt wurde
// V1.11 Angsttimer hinzugefügt. Resetet Req nach 10s wenn nötig
VERSION : '1.11'


VAR_INPUT
  DB_1_Recv : ANY ;    // Daten die empfangen werden sollen DB, ANY oder Struct
  DB_1_Send : ANY ;    // Daten die gesendet werden
      ID : WORD ;    //Lokale VerbindungsID aus Netpro
  Master : BOOL; // Eine seite des OBJ_MasterCom muss master sein, sonst passen die KoppelID nicht
END_VAR
VAR_OUTPUT
  zykl : TIME ;  // Zyklus in denen ein Telegramm gesendet wird beendet wird  
  WD: BOOL;   // Wenn Senden oder Empfangen länger als 5sek kein Done liefert, Error.
END_VAR
VAR
  SEND : "BSEND";    
  RECV : "BRCV";    
  save_status_SEND : WORD ;    
  save_status_RECV : WORD ;    
  Zeit_alt : TIME ;    
  Watchdog_Empfangen : TON;
  Watchdog_Senden : TON;
  Angsttimer : TON;
  
END_VAR
VAR_TEMP
  zDB_1_Recv : ANY ;  
        pDB_1_Recv AT zDB_1_Recv :  STRUCT  
                                       SyntaxID : BYTE ;    
                                       Bereichstyp : BYTE ;    
                                       Anzahl_Werte : INT ;    
                                       DB_Nr : INT ;    
                                       Startadresse : DWORD ;    
                                    END_STRUCT ;    
  zDB_1_Send : ANY ;
        pDB_1_Send AT zDB_1_Send : STRUCT  
                                       SyntaxID : BYTE ;    
                                       Bereichstyp : BYTE ;    
                                       Anzahl_Werte : INT ;    
                                       DB_Nr : INT ;    
                                       Startadresse : DWORD ;    
                                    END_STRUCT ;    
  wDummy : WORD ;    
  RetVAL : INT ;    
  Write_Prot : BOOL ;    
  Zeit : TIME ;  
  Anzahl_Werte_RECV:  WORD;  
  Anzahl_Werte_SEND:  WORD;  


END_VAR
zDB_1_Recv := DB_1_Recv;
zDB_1_Send := DB_1_Send;


Zeit := TIME_TCK();


(*Pointer aufbereiten*)
IF pDB_1_Recv.Bereichstyp = B#16#19 then
(*Empfangen*)
  pDB_1_Recv.syntaxid         := B#16#10;                                 // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_Recv.Bereichstyp      := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_Recv.DB_Nr            := DWORD_TO_INT(pDB_1_Recv.Startadresse);  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(pDB_1_Recv.Startadresse), DB_LENGTH := Anzahl_Werte_RECV ,WRITE_PROT := Write_Prot);
  pDB_1_Recv.Anzahl_Werte     := WORD_TO_INT(Anzahl_Werte_RECV);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  pDB_1_Recv.Startadresse     := DW#16#84000000;  
END_IF;  
  


IF pDB_1_send.Bereichstyp = B#16#19 then
  (*Senden*)
  pDB_1_send.syntaxid         := B#16#10;                                 // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_send.Bereichstyp      := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
  pDB_1_send.DB_Nr            := DWORD_TO_INT(pDB_1_send.Startadresse);  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(pDB_1_send.Startadresse), DB_LENGTH := Anzahl_Werte_SEND ,WRITE_PROT := Write_Prot);
  pDB_1_send.Anzahl_Werte     := WORD_TO_INT(Anzahl_Werte_SEND);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
  pDB_1_send.Startadresse     := DW#16#84000000;  
END_IF;  
  
// Senden des Datenbausteins










Anzahl_Werte_SEND := INT_TO_WORD(pDB_1_send.Anzahl_Werte);
IF Master THEN 
    SEND.R_ID := w#16#1;
ELSE
    SEND.R_ID := w#16#2;
END_IF;


  SEND(ID := ID,
    SD_1 := zDB_1_Send,
     LEN :=  Anzahl_Werte_SEND); 


Angsttimer(IN :=SEND.REQ,
            PT := t#10s);


SEND.REQ := TRUE;


IF SEND.ERROR OR SEND.DONE OR Angsttimer.q THEN
    SEND.Req := FALSE;
END_IF;


IF SEND.ERROR THEN
    save_status_SEND := SEND.STATUS; 
END_IF;      


IF SEND.done THEN
    Zykl := Zeit - Zeit_alt;
    zeit_alt := zeit;
END_IF;
            
Watchdog_Senden(IN := NOT SEND.DONE, PT := t#5s);


// Empfangen des Datenbausteins
IF Master THEN 
    RECV.R_ID := w#16#2;
ELSE
    RECV.R_ID := w#16#1;
END_IF;
    
    
    
    
      RECV(EN_R := TRUE // IN: BOOL
           ,ID := ID // IN: WORD
           ,RD_1 := zDB_1_Recv // INOUT: ANY
           ,LEN :=  Anzahl_Werte_Recv // INOUT: WORD
           ); 
        
          IF RECV.ERROR THEN
           save_status_RECV := RECV.STATUS; 
          END_IF;  


Watchdog_Empfangen(IN := NOT RECV.NDR, PT := t#5s);


WD := Watchdog_empfangen.q OR Watchdog_Senden.q;


END_FUNCTION_BLOCK

Diesen hier bastelt man in der 1500er rein
Code:
FUNCTION_BLOCK "MasterCom"
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : Vor
FAMILY : Com
VERSION : 1.11
// Dieser Baustein regelt die Kommunikation zu einer Partnerstation Slave
// Es ist pro S7 Verbindung nur ein MasterCom zulässig da die Koppel_ID im Baustein vergeben wird
// Da BSend und BRCV verwendet werden sind nur S7 Verbindungen erlaubt
// Nach möglichkeit sind die SFB12/13 BSEND/BRCV zu verwenden, wenn nicht durch die CPU unterstützt
// dann die FB12/13 aus der Kommunikationsbibliothek Step7 in die CPU laden. 
// SFB und FB arbeiten zusammen
//
// V1.0 Startversion
// V1.1 Umgebaut auf ANY_Pointer
// V1.2 ANY in Baustein Integriert (Structur gebildet)
// V1.10 Baustein erkennt jetzt selbstständig ob Ein DB, Pointer oder Structur angehängt wurde
//v1.11 Angsstimer hinzugefügt. Resetet den Request wenn er länger als 10sek ansteht.
   VAR_INPUT 
      DB_1_Recv : Any;   // Daten die empfangen werden sollen DB, ANY oder Struct
      DB_1_Send : Any;   // Daten die gesendet werden
      ID : Word;   // Lokale VerbindungsID aus Netpro
      Master : Bool;   // Eine seite des OBJ_MasterCom muss master sein, sonst passen die KoppelID nicht
   END_VAR


   VAR_OUTPUT 
      zykl : Time;   // Zyklus in denen ein Telegramm gesendet wird beendet wird
      WD : Bool;   // Wenn Senden oder Empfangen länger als 5sek kein Done liefert, Error.
   END_VAR


   VAR 
      SEND {OriginalPartName := 'BSEND_SFB_PART'; LibVersion := '1.2'} : BSEND;
      RECV {OriginalPartName := 'BRCV_SFB_PART'; LibVersion := '1.2'} : BRCV;
      save_status_SEND : Word;
      save_status_RECV : Word;
      Zeit_alt : Time;
      Watchdog_Empfangen {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
      Watchdog_Senden {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
      Angsttimer {OriginalPartName := 'IEC_TIMER'; LibVersion := '1.0'} : TON_TIME;
      tReset : Bool;
   END_VAR


   VAR_TEMP 
      zDB_1_Recv : Any;
      pDB_1_Recv AT zDB_1_Recv : Struct
         SyntaxID : Byte;
         Bereichstyp : Byte;
         Anzahl_Werte : Int;
         DB_Nr : Int;
         Startadresse : DWord;
      END_STRUCT;
      zDB_1_Send : Any;
      pDB_1_Send AT zDB_1_Send : Struct
         SyntaxID : Byte;
         Bereichstyp : Byte;
         Anzahl_Werte : Int;
         DB_Nr : Int;
         Startadresse : DWord;
      END_STRUCT;
      wDummy : Word;
      RetVAL : Int;
      Write_Prot : Bool;
      Zeit : Time;
      Anzahl_Werte_RECV : Word;
      Anzahl_Werte_SEND : Word;
      SCL_MIGRA_TEMP_INT_1 : Int;
      Migra_DB_LENGTH : UDInt;
      Attrib_RCV : Byte;
      Attrib_SND : Byte;
      uiAnzahl_Werte_RECV : UDInt;
      uiAnzahl_Werte_SEND : UDInt;
   END_VAR




BEGIN
	#zDB_1_Recv := #DB_1_Recv;
	#zDB_1_Send := #DB_1_Send;
	
	#Zeit := TIME_TCK();
	
	(*Pointer aufbereiten*)
	IF #pDB_1_Recv.Bereichstyp = B#16#19 THEN
	(*Empfangen*)
	  #pDB_1_Recv.SyntaxID := B#16#10;                                 // Typ des Bereichs soll Byte sein (INT = 2)
	  #pDB_1_Recv.Bereichstyp := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
	  #pDB_1_Recv.DB_Nr := DWORD_TO_INT(#pDB_1_Recv.Startadresse);  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
	  (* classic code: #RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(#pDB_1_Recv.Startadresse), DB_LENGTH => #Anzahl_Werte_RECV, WRITE_PROT => #Write_Prot);*)
	  #SCL_MIGRA_TEMP_INT_1:=ATTR_DB(REQ:=True,DB_NUMBER:=UINT_TO_DB_ANY(DWORD_TO_UINT(#pDB_1_Recv.Startadresse)) ,DB_LENGTH=>#uiAnzahl_Werte_RECV, ATTRIB=>#Attrib_RCV);
	  #Anzahl_Werte_RECV:=UDINT_TO_WORD(#uiAnzahl_Werte_RECV);
	#RetVAL := #SCL_MIGRA_TEMP_INT_1;
	#pDB_1_Recv.Anzahl_Werte := UDINT_TO_INT(#Anzahl_Werte_RECV);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
	  #pDB_1_Recv.Startadresse := DW#16#84000000;
	END_IF;
	
	IF #pDB_1_Send.Bereichstyp = B#16#19 THEN
	(*Senden*)
	  #pDB_1_Send.SyntaxID := B#16#10;                                 // Typ des Bereichs soll Byte sein (INT = 2)
	  #pDB_1_Send.Bereichstyp := 2;                                         // Typ des Bereichs soll Byte sein (INT = 2)
	  #pDB_1_Send.DB_Nr := DWORD_TO_INT(#pDB_1_Send.Startadresse);  // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
	  (* classic code: #RetVAL := TEST_DB(DB_NUMBER := DWORD_TO_WORD(#pDB_1_Send.Startadresse), DB_LENGTH => #Anzahl_Werte_SEND, WRITE_PROT => #Write_Prot);*)
	  #SCL_MIGRA_TEMP_INT_1:=ATTR_DB(REQ:=True,DB_NUMBER:=UINT_TO_DB_ANY(DWORD_TO_UINT(#pDB_1_Send.Startadresse)) ,DB_LENGTH=>#uiAnzahl_Werte_SEND,ATTRIB=>#Attrib_SND);
	  #Anzahl_Werte_SEND:=UDINT_TO_WORD(#uiAnzahl_Werte_SEND);
	#RetVAL := #SCL_MIGRA_TEMP_INT_1;
	  #pDB_1_Send.Anzahl_Werte := UDINT_TO_INT(#Anzahl_Werte_SEND);             // Länge des Pointers weil wir einen Any ohne Länge angeben wollen
	  #pDB_1_Send.Startadresse := DW#16#84000000;
	END_IF;
	
	// Senden des Datenbausteins
	
	
	
	
	
	#Anzahl_Werte_SEND := INT_TO_WORD(#pDB_1_Send.Anzahl_Werte);
	IF #Master THEN
	  #SEND.R_ID := 1;
	ELSE
	  #SEND.R_ID := 2;
	END_IF;
	
	#SEND(ID:=#ID,SD_1:=#zDB_1_Send,LEN:=#Anzahl_Werte_SEND);
	
	#Angsttimer(IN :=#SEND.REQ,
	            PT := t#10s,
	            Q=>#tReset);
	
	#SEND.REQ := TRUE;
	
	IF #SEND.ERROR OR #SEND.DONE OR #tReset THEN
	  #SEND.REQ := FALSE;
	END_IF;
	
	IF #SEND.ERROR THEN
	  #save_status_SEND := #SEND.STATUS;
	END_IF;
	
	IF #SEND.DONE THEN
	  #zykl := #Zeit - #Zeit_alt;
	  #Zeit_alt := #Zeit;
	END_IF;
	
	#Watchdog_Senden(IN := NOT #SEND.DONE,
	                 PT := t#5s);
	
	// Empfangen des Datenbausteins
	IF #Master THEN
	  #RECV.R_ID := 2;
	ELSE
	  #RECV.R_ID := 1;
	END_IF;
	
	
	
	
	#RECV(EN_R:=TRUE,ID:=#ID,
	      RD_1:=#zDB_1_Recv,
	      LEN:=#Anzahl_Werte_RECV);
	
	IF #RECV.ERROR THEN
	  #save_status_RECV := #RECV.STATUS;
	END_IF;
	
	#Watchdog_Empfangen(IN := NOT #RECV.NDR,
	                    PT := t#5s);
	
	#WD := #Watchdog_Empfangen.Q OR #Watchdog_Senden.Q;
	
END_FUNCTION_BLOCK

So ist auf jedenfall eine Kommunikation per BSEND RECV möglich.

will man mehr als ein BSEND RECV paar auf einer Verbindung laufen lassen, ist es notwendig das R_ID Paar nach draussen zu führen. Ich wollte das hier aber möglichst einfach halten.

Die Bausteine sind getestet und funktionieren grundsätzlich.
 
s7-verbindung_1500_3.jpg
s7-verbindung_1500_4.jpg

Und logischerweise müssen die zu übertragenden Strukturen nicht optimiert und von gleicher länge sein.

Aufruf in der 1500er
Code:
"MasterCom_DB"(DB_1_Recv := "von300", // DB22
               DB_1_Send := "an300", // DB20
               ID        := 16#102,
               Master    := TRUE,
               zykl      => "zykl",
               WD        => "Watchdog");

in der 300er
Code:
"MasterCom_DB"(DB_1_Recv := "an300",
               DB_1_Send := "von300",
               ID        := 16#1,
               Master    := false,
               zykl      => "tykl",
               WD        => "Watchdog");

Mit leichten Anpassungen lässt sich er MasterCom für die 300er auch in die Classic Step7 Welt portieren.
 
SimaticACC war auf der lokalen Seite angehakt. Partner VerbRsource 03

Ich habe dann mal SimaticACC abgehakt und dort 10 eingetragen. Partner hatte ich auf 03 gelassen.
Verbindung wird aufgebaut aber Send/Rcv geht nicht. PUT/GET schon.

Auch mal auf beiden Seiten SimaticACC angehakt.
Lokal SimaticACC10201, Partner SimaticACC10201. Verbindung wurde nicht aufgebaut.
Wobei sich hier wohl die TSAP aus SimaticACC & ID & 01 zusammensetzt.

Dann habe ich mir mal die spezifizierte Verbindung, deine Screenshots und eine AGSend/AGRcv-Verbindung näher angesehen.

Ich schlussfolger daraus
Für eine PUT/GET Kommunikation ist die Partner TSAP egal.
Für eine 2-seitige Verbindung (AGSend/AGRcv und BSend/BRcv) müssen die gegenseitigen TSAP's übereinstimmen.
Entweder beiden gleich oder LokalerTSAP 1 PartnerTSAP 2. Auf der anderen Seite dann LokalerTSAP 2 PartnerTSAP 1.
Ist das so? Ich denke schon.

Durch die ganze Recourcenänderei hab ich nun eine Onlinverbindung (rot) die es offline gar nicht mehr gibt.
Die neue Verbindung die ich angelegt habe ist nur offline verfügbar.
Mehrmals die HW neu übertragen. MMC formatiert und MRes durchgeführt. Immer noch da.
Dann hatte ich die Schnauze voll und hab Feierabend gemacht. Morgen gehts weiter.

Aber ich glaube so langsam verstehe ich die ganze Sache immer besser.

Deine Bausteine schaue ich mir morgen dann auch mal genauer an.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich schlussfolger daraus
Für eine PUT/GET Kommunikation ist die Partner TSAP egal.

bei put get muss man ja auch nur die Seite definieren auf der put get läuft. Die andere cpu muss nichtmal auf run sein und auch keine Verbindungen geladen haben.
Bei zweiseitigen Verbindungen gebe ich aus Prinzip auf beiden Seiten dieselbe verbindungsrecource an. Acc benutze ich nie. Weis ehrlichgesagt noch gar nicht wozu das gut ist.
 
Acc benutze ich nie. Weis ehrlichgesagt noch gar nicht wozu das gut ist.

weiss ich auch nicht ob das weiterreichende folgen hat
vlt ist das auch einfach nur eine bezeichnung für den tsap. SimaticAcc + Id + 01. z.b. SimaticAcc10201
ähnlich wie bei den IsoOnTCP wo Tia TCP-1 usw als TSAp vorschlägt.
 
also acc darf nicht angehakt sein. Und Verbindungsrecource 03 würd ich auch nicht nehmen, damit ist auch irgendwas anders. Also ab 10 sollte ok sein.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
das problem war der tsap.
mir war bis dato nicht so richtig klar wie das ganze funktioniert.
hatte bisher nur einseitige verbindungen genutzt (put/get)
da ist der tsap des partners ja egal wenn das auf 03 steht.
(hab heute noch was im inet gefunden was 01(pg-verbindung) 02(op-verbindung) bedeutet. das werde ich morgen spasshalber mal testen)
 
das problem war der tsap.
mir war bis dato nicht so richtig klar wie das ganze funktioniert.

Naja der TSAP gibt ja einfach die Verbindungsrecource an. Welche mit der des Partners übereinstimmen muss, ich habe immer 10 oder höher genommen. die Zahl hinter dem Punkt gibt den Slot der CPU im Ziel und auch lokal an. Bei der 1500er ist das ja Slot 1 bei der 300 Slot 2 und bei der 400 entsprechend dem Steckplatz.

Die Verbindungsrecource selber kann man also an beiden Endpunkten gleich wählen, und der Slot ist meistens eh gegeben. Das macht es etwas einfacher das mit einem fremden Partner abzusprechen.
 
Zurück
Oben