stefano_PLC Inserito: 21 novembre 2019 Segnala Inserito: 21 novembre 2019 Buongiorno, su una cpu s7-1200 sto istallando oltre alla lampada dell' avaria una segnalazione acustica (cicalino), solamente che quando do il reset a prescindere se ho risolto le mie avarie o meno deve cessare il cicalino, idee su come farlo? Perchè io quando sono in avaria setto il bit del cicalino, quando premo il reset rilevo il fronte di salita e resetto i l bit, il mio dubbio sorge quando non ho risolto le avarie e quindi mi rimane settato il cicalino, come posso risolvere questa cosa? grazie in anticipo
stefano_PLC Inserita: 21 novembre 2019 Autore Segnala Inserita: 21 novembre 2019 (modificato) Metodo che ho applicato per risolvere, secondo voi è giusto? Personalmente ho fatto un confronto salvandomi il codice dell' avaria in una variabile di appoggio quando do il reset, quindi confronto il codice avaria è diverso dalla variabile di appoggio abilito il cicalino, se sono uguali non si ecciterà la bobina e quindi non suonerà, ovviamente ogni volta che si eccita la bobina pulisco i miei campi di appoggio. Modificato: 21 novembre 2019 da stefano_PLC
pigroplc Inserita: 21 novembre 2019 Segnala Inserita: 21 novembre 2019 prova questo codice FUNCTION "MEM_OR_AND" : Bool { S7_Optimized_Access := 'FALSE' } VERSION : 0.1 VAR_INPUT IN1 : Any; IN2 : Any; boAND_OR : Bool; // FALSE = AND; TRUE = OR END_VAR VAR_TEMP RetVal : Int; IN1End : DInt; // End addresses for memory areas IN2End : DInt; Tst1 : Byte; Tst2 : Byte; MemPtr : DInt; // Current memory pointer pIN1 : Any; // DB address ANY pointer - used for accessing DB data pAnyIN1 AT pIN1 : Struct // Diassembled ANY pointer structure S7Code : Byte; // Code for S7 (fixed at 16#10) DataType : Byte; // Code for data type Length : Int; // Repetition factor = Send/receive length DBNumber : Int; // Data block Number MemoryArea : Byte; // Specified memory area = 0x84 = data block ByteAddressMSB : Byte; // Byte address most significant bits ByteAddressLSB : Word; // Byte address least significant bits END_STRUCT; u1Addr : DInt; // Union pointer for calculation of byte offset address u1 AT u1Addr : Struct // This is equivalent to a union in 'c' dword goes in and word / byte LSB / byte MSB come out ByteAddrLSBPad : Byte; // Not Used ByteAddrMSB : Byte; // Byte address most significant bits WordAddr : Word; // Byte address least significant bits END_STRUCT; pIN2 : Any; // DB address ANY pointer - used for accessing DB data pAnyIN2 AT pIN2 : Struct // Diassembled ANY pointer structure S7Code : Byte; // Code for S7 (fixed at 16#10) DataType : Byte; // Code for data type Length : Int; // Repetition factor = Send/receive length DBNumber : Int; // Data block Number MemoryArea : Byte; // Specified memory area = 0x84 = data block ByteAddressMSB : Byte; // Byte address most significant bits ByteAddressLSB : Word; // Byte address least significant bits END_STRUCT; u2Addr : DInt; // Union pointer for calculation of byte offset address u2 AT u2Addr : Struct // This is equivalent to a union in 'c' dword goes in and word / byte LSB / byte MSB come out ByteAddrLSBPad : Byte; // Not Used ByteAddrMSB : Byte; // Byte address most significant bits WordAddr : Word; // Byte address least significant bits END_STRUCT; END_VAR BEGIN (* effettua l'OR logico oppure l'AND logico fra 2 aree di memoria e lo mette sull'area del secondo parametro esempio utile se si vuole gestire il fronte di salita di un bit della prima area basta far ciclare una volta l'OR logico quindi equalizzare le aree. Con l'AND logico si azzerano anche gli allarmi della seconda area ESEMPIO: si vuole far suonare una sirena ogni volta che un allarme nuovo si verifica: 1) richiamare il blocco MEM_OR_AND con il bit boAND_OR FALSE in modo da aggiornare la seconda area se un allarme cessa di esistere 2) richiamare il blocco MEMCMP con il suo flag di uscita IN NEGATO collegato alla sirena (si attiva ogni volta che le aree sono diverse) 3) sul fronte di salita del pulsante di tacitazione collegare il bit boAND_OR TRUE in modo da aggiornare la seconda area come la prima, quindi l'uscita MEMCMP ritorna FALSE *) #pIN1 := #IN1; // appoggia il puntatore #pAnyIN1.DataType := 16#02; // forza il formato dei byte #u1.ByteAddrMSB := #pAnyIN1.ByteAddressMSB; // aggiorna i puntatori #u1.WordAddr := #pAnyIN1.ByteAddressLSB; #IN1End := #u1Addr + (#pAnyIN1.Length * 8); // aggiorna la fine shiftata dei 3 bit del puntatore #pAnyIN1.Length := 1; // confronta solamente un byte alla volta #pIN2 := #IN2; // idem per area di confronto #pAnyIN2.DataType := 16#02; #u2.ByteAddrMSB := #pAnyIN2.ByteAddressMSB; #u2.WordAddr := #pAnyIN2.ByteAddressLSB; #IN2End := #u2Addr + (#pAnyIN2.Length * 8); #pAnyIN2.Length := 1; // fault parameters IF (#IN1End - #u1Addr) <> (#IN2End - #u2Addr) THEN #MEM_OR_AND:= TRUE; // errore dei parametri, valore di ritorno a uno RETURN; END_IF; WHILE (#u1Addr < #IN1End) AND (#u2Addr < #IN2End) DO // confronto fino alla fine dei bytes #RetVal := BLKMOV(SRCBLK := #pIN1, DSTBLK => #Tst1);// legge i byte dal primo all'ultimo IF #RetVal <> 0 THEN #MEM_OR_AND := TRUE; // errore di lettura, valore di ritorno a uno RETURN; END_IF; #RetVal := BLKMOV(SRCBLK := #pIN2, DSTBLK => #Tst2); IF #RetVal <> 0 THEN #MEM_OR_AND := TRUE; // errore di lettura, valore di ritorno a uno RETURN; END_IF; (* esamina l'AND o l'OR in funzione del parametro di ingresso per ripulire la seconda area o per aggiornare la seconda area come la prima *) IF #boAND_OR THEN #Tst2 := #Tst1 OR #Tst2; ELSE #Tst2 := #Tst1 AND #Tst2; END_IF; (* quindi rinfresca il byte ..... e aggiorna i parametri *) #RetVal := BLKMOV(SRCBLK := #Tst2, DSTBLK => #pIN2 ); #u1Addr := #u1Addr + 8; // incremento dei puntatori #pAnyIN1.ByteAddressMSB := #u1.ByteAddrMSB; #pAnyIN1.ByteAddressLSB := #u1.WordAddr; #u2Addr := #u2Addr + 8; #pAnyIN2.ByteAddressMSB := #u2.ByteAddrMSB; #pAnyIN2.ByteAddressLSB := #u2.WordAddr; END_WHILE; #MEM_OR_AND := FALSE; END_FUNCTION il richiamo invece è questo: "boErroreParametri" := "MEM_OR_AND"(IN1 := P#DB235.DBX50.0 BYTE 30, IN2 := P#DB236.DBX50.0 BYTE 30, boAND_OR := "boAND"); da un punto di vista concettuale tu hai 2 aree bitmap che dedichi agli allarmi. uno è quello degli allarmi attivi e l'altro è quello degli allarmi tacitati. L'esempio verifica 30 bytes fra le DB235 e DB236 buon divertimento
pigroplc Inserita: 21 novembre 2019 Segnala Inserita: 21 novembre 2019 questo è il blocco MEMCP FUNCTION "MEMCMP" : Bool { S7_Optimized_Access := 'FALSE' } VERSION : 0.1 VAR_INPUT IN1 : Any; IN2 : Any; END_VAR VAR_TEMP RetVal : Int; IN1End : DInt; // End addresses for memory areas IN2End : DInt; Tst1 : Byte; Tst2 : Byte; MemPtr : DInt; // Current memory pointer pIN1 : Any; // DB address ANY pointer - used for accessing DB data pAnyIN1 AT pIN1 : Struct // Diassembled ANY pointer structure S7Code : Byte; // Code for S7 (fixed at 16#10) DataType : Byte; // Code for data type Length : Int; // Repetition factor = Send/receive length DBNumber : Int; // Data block Number MemoryArea : Byte; // Specified memory area = 0x84 = data block ByteAddressMSB : Byte; // Byte address most significant bits ByteAddressLSB : Word; // Byte address least significant bits END_STRUCT; u1Addr : DInt; // Union pointer for calculation of byte offset address u1 AT u1Addr : Struct // This is equivalent to a union in 'c' dword goes in and word / byte LSB / byte MSB come out ByteAddrLSBPad : Byte; // Not Used ByteAddrMSB : Byte; // Byte address most significant bits WordAddr : Word; // Byte address least significant bits END_STRUCT; pIN2 : Any; // DB address ANY pointer - used for accessing DB data pAnyIN2 AT pIN2 : Struct // Diassembled ANY pointer structure S7Code : Byte; // Code for S7 (fixed at 16#10) DataType : Byte; // Code for data type Length : Int; // Repetition factor = Send/receive length DBNumber : Int; // Data block Number MemoryArea : Byte; // Specified memory area = 0x84 = data block ByteAddressMSB : Byte; // Byte address most significant bits ByteAddressLSB : Word; // Byte address least significant bits END_STRUCT; u2Addr : DInt; // Union pointer for calculation of byte offset address u2 AT u2Addr : Struct // This is equivalent to a union in 'c' dword goes in and word / byte LSB / byte MSB come out ByteAddrLSBPad : Byte; // Not Used ByteAddrMSB : Byte; // Byte address most significant bits WordAddr : Word; // Byte address least significant bits END_STRUCT; END_VAR BEGIN (* confronta un numero variabile di bytes in modo indiretto facendo un loop col WHILE utilizza la funzione AT che permette l'accesso all'area precedente con il formato dell'area successiva: nel primo richiamo si accede all'area pIN1 dichiarata come ANY con il formato pAnyIN1 che è una struttura formattata con i parametri ANY I puntatori ANY formattati hanno la struttura del puntatore ANY di 10 byte. Questo è sovrapposto alla variabile pointer ANY usando il comando AT. Ciò consente di utilizzare un puntatore ANY ed estrarre i singoli elementi di dati del tipo di puntatore ANY. È vero anche il contrario; è quindi possibile modificare i singoli elementi di dati del tipo di puntatore ANY e quindi utilizzare il puntatore ANY risultante. L'indirizzo utilizzato nel tipo di puntatore ANY è codificato a bit e byte. Vale a dire accedere alla memoria byte per byte, devi rendere l'indirizzo un multiplo di 8. L'accesso ai byte è obbligatorio altrimenti il BLKMOV non funziona a meno che tu non stia spostando byte completi. Si noterà che l'indirizzo è composto da 32 bit (2 parole), ma in realtà vengono usati solo 19 bit (i primi 13 bit sono sempre zeri). Quindi l'indirizzo massimo a cui è possibile accedere è 2 ^ 21 bit = 2097152 bit o 2 ^ 18 byte (2097152/8) = 262144 byte. Per calcolare l'indirizzo per un byte definito, dovremmo prendere l'indirizzo di byte e il bit spostarlo a destra di 3 per convertirlo in un indirizzo bit, quindi creare le parole da inserire nella posizione dell'indirizzo del puntatore ANY. Questo sarebbe un lavoro arduo perché significherebbe spostare i bit shiftati da una parola e copiarli alla successiva. La soluzione quindi è di creare un'altra struttura sovrapposta (u1) che si sovrapponga a 24 bit (un byte e una parola) su una variabile DINT (u1addr), quindi il lavoro è già fatto. La prima cosa è caricare la variabile u1addr con l'indirizzo di base (start) dell'indirizzo di input in questo modo: - u1.ByteAddrMSB: = pAnyIN1.ByteAddressMSB; u1.WordAddr: = pAnyIN1.ByteAddressLSB; u1addr sarà uguale all'indirizzo di base (avvio) mappata bit a bit. Esegue anche gli indirizzi finali semplicemente utilizzando i dati forniti dal valore della lunghezza del puntatore ANY pAnyIN1.Length (prima di cambiarlo). Quando puntato su un DB, questo verrà impostato sulla dimensione del DB in byte (viene fatto in automatico). Quindi semplicemente moltiplicare questo valore per 8 per convertirlo in indirizzo bit a bit e quindi aggiungerlo all'indirizzo di base (avvio) calcolato sopra Ora tutto quello da fare è incrementare la variabile u1addr byte a byte (in multipli di 8) e poi si otengono i valori di indirizzo disponibili in u1.ByteAddrMSB e u1.WordAddr da caricare nel parametro ANY uguale alla struttura dell'indirizzo puntatore (rispettivamente pAnyIN1.ByteAddressMSB e pAnyIN1.ByteAddressLSB) per impostare il puntatore ANY per accedere al nuovo indirizzo. Quindi la linea: pIN1: = IN1; Carica l'input del puntatore di memoria (nel tuo caso un indirizzo DB) nel pointer ANY pIN1, che può essere un accesso / manipolato dal puntatore ANY mappato pAnyIN1. Il ciclo termina quando l'intervallo di input di origine o di destinazione coincidono (consentendo di confrontare i DB di dimensioni o sottotipi diversi all'interno di altri tipi). Se non si desidera eseguire questa operazione e si desidera confrontare solo tipi identici, è possibile aggiungere un controllo per verificare se le due dimensioni non sono uguali, quindi restituire false immediatamente prima di avviare il ciclo. Così: IF (IN1End - u1addr) <> (IN2End - u2addr) THEN MEMCMP: = FALSE; RETURN; END_IF; *) #pIN1 := #IN1; // appoggia il puntatore #pAnyIN1.DataType := 16#02; // forza il formato dei byte #u1.ByteAddrMSB := #pAnyIN1.ByteAddressMSB; // aggiorna i puntatori #u1.WordAddr := #pAnyIN1.ByteAddressLSB; #IN1End := #u1Addr + (#pAnyIN1.Length * 8); // aggiorna la fine shiftata dei 3 bit del puntatore #pAnyIN1.Length := 1; // confronta solamente un byte alla volta #pIN2 := #IN2; // idem per area di confronto #pAnyIN2.DataType := 16#02; #u2.ByteAddrMSB := #pAnyIN2.ByteAddressMSB; #u2.WordAddr := #pAnyIN2.ByteAddressLSB; #IN2End := #u2Addr + (#pAnyIN2.Length * 8); #pAnyIN2.Length := 1; WHILE (#u1Addr < #IN1End) AND (#u2Addr < #IN2End) DO // confronto fino alla fine dei bytes #RetVal := BLKMOV(SRCBLK := #pIN1, DSTBLK => #Tst1);// legge i byte dal primo all'ultimo IF #RetVal <> 0 THEN #MEMCMP := FALSE; // errore di lettura, valore di ritorno a zero RETURN; END_IF; #RetVal := BLKMOV(SRCBLK := #pIN2, DSTBLK => #Tst2); IF #RetVal <> 0 THEN #MEMCMP := FALSE; // errore di lettura, valore di ritorno a zero RETURN; END_IF; IF #Tst1 <> #Tst2 THEN #MEMCMP := FALSE; // differenza di byte, valore di ritorno a zero RETURN; END_IF; #u1Addr := #u1Addr + 8; // incremento dei puntatori #pAnyIN1.ByteAddressMSB := #u1.ByteAddrMSB; #pAnyIN1.ByteAddressLSB := #u1.WordAddr; #u2Addr := #u2Addr + 8; #pAnyIN2.ByteAddressMSB := #u2.ByteAddrMSB; #pAnyIN2.ByteAddressLSB := #u2.WordAddr; END_WHILE; #MEMCMP := TRUE; END_FUNCTION tutto questo l'ho fatto per puro esercizio, alla fine non mi è mai servito, chissà mai se serve a qualcuno di voi...... Certo ci sarà il modo di farlo anche più semplice. E se le critiche sono costruttive, benvengano.
stefano_PLC Inserita: 21 novembre 2019 Autore Segnala Inserita: 21 novembre 2019 Ti ringrazio ma non ci capisco nulla di quello che dovrebbe fare sto codice, e più che PLC mi sembra molto c#.
pigroplc Inserita: 22 novembre 2019 Segnala Inserita: 22 novembre 2019 (modificato) 15 ore fa, stefano_PLC ha scritto: Ti ringrazio ma non ci capisco nulla di quello che dovrebbe fare sto codice, e più che PLC mi sembra molto c#. 😁😁😁 SCL e lo puoi inserire nel tuo 1200 (non uso il 1200 ma se non erro non dovrebbero esserci problemi). Quanto a capire il codice, non fosse che metto commenti su commenti non ci capirei più nulla pure io. 😁 Modificato: 22 novembre 2019 da pigroplc
dperla Inserita: 22 novembre 2019 Segnala Inserita: 22 novembre 2019 Buongiorno, ti sconsiglio di legare l'uscita del cicalino ad una memoria gestita con set/reset, ma di gestirla tramite bobina. Se vuoi che dopo un certo tempo debba smettere (perchè è fastidioso...) inserisci al limite un temporizzatore. Se l'avaria non è stata risolta, dopo alcuni minuti puoi farla tornate a bippare. Ad esempio: - Attiva per 5 sec - Pausa 5 minuti - Attiva per 5 sec ... Puoi farlo anche in ladder con gli opportuni temporizzatori
Messaggi consigliati
Crea un account o accedi per commentare
Devi essere un utente per poter lasciare un commento
Crea un account
Registrati per un nuovo account nella nostra comunità. è facile!
Registra un nuovo accountAccedi
Hai già un account? Accedi qui.
Accedi ora