Vai al contenuto
PLC Forum


Utilizzo istruzione AT


Cesare Nicola

Messaggi consigliati

Cesare Nicola

Qualcuno mi può spiegare con un esempio pratico (a parole, non intendo un software di esempio) quando e perché utilizzare l'istruzione AT, che non conosco? Quali vantaggi ha?
Grazie

Link al commento
Condividi su altri siti


io la trovo utile in SCL

 

se hai una variabile "var1" dichiarata DWORD e una variabile var2 definita "Array [0..31] AT Var1"

 

puoi fare var2[5] per accedere al bit 5 di var1. 

 

 

Link al commento
Condividi su altri siti

Ciao Nicola,

è un po' che non ci sentiamo, 

a livello pratico serve a spacchettare una area di ingresso definita come puntatore. Un esempio pratico è il codice di BitCounter qui sotto riportato. 

T fai la sorgente SCL e te la importi in TIA.

 

Il codice è a disposizione di tutti, ciò che chiedo è di non rimuovere il nome dell'autore.

 

FUNCTION "BitCounter" : Byte
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : pigroplc
VERSION : 0.1
   VAR_INPUT 
      IN : Any;
   END_VAR

   VAR_OUTPUT 
      i32BitCounter : DInt;
   END_VAR

   VAR_TEMP 
      RetVal : Int;
      Tst : Byte;
      INEnd : DInt;   // End addresses for memory areas
      pIN : Any;   // DB address ANY pointer - used for accessing DB data
      pAnyIN AT pIN : 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;
   END_VAR


BEGIN
	(*
	conta i bit che sono = 1 in un'area dichiarata come parametro di ingresso 
	e restituisce un byte di errore in caso di parametro in ingresso errato 
	la sintassi del richiamo è nel seguente esempio:
	"byteError":= "BitCounter"(IN:=P#DB235.DBX50.0 BYTE 30, i32BitCounter=>"ConteggioBit");
	dove 
	"byteError" è il byte di errore se diverso da zero = errore
	"ConteggioBit" numero di bit = 1
	P#DB235.DBX50.0 BYTE 30 = controlla la DB235 dal byte 50 compreso per 30 bytes
	*)
	
	
	#i32BitCounter := 0;    // azzera il contabit in default
	
	#pIN := #IN;    // copia nell'area interna
	
	IF #pAnyIN.DBNumber = 0 THEN
	    #BitCounter := 1; // errore di parametro numero di DB
	    RETURN;
	END_IF;
	IF #pAnyIN.MemoryArea <> 16#84 THEN
	    #BitCounter := 2; // errore di parametro di DB 
	    RETURN;
	END_IF;
	
	
	#u1.ByteAddrMSB := #pAnyIN.ByteAddressMSB;     // aggiorna i puntatori
	#u1.WordAddr := #pAnyIN.ByteAddressLSB;
	#INEnd := #u1Addr + (#pAnyIN.Length * 8);      // aggiorna la fine shiftata dei 3 bit del puntatore 
	#pAnyIN.Length := 1;                           // confronta solamente un byte alla volta
	
	
	// adesso legge 1 byte alla volta indicizzato dinamicamente e controlla come prima cosa che non ci siano errori
	// quindi confronta il byte con zero
	
	WHILE (#u1Addr < #INEnd)  DO                    // confronto fino alla fine dei bytes
	    #RetVal := BLKMOV(SRCBLK := #pIN, DSTBLK => #Tst);// legge i byte dal primo all'ultimo 
	    IF #RetVal <> 0 THEN
	        #BitCounter := 3;                       // errore di lettura, valore di ritorno a zero
	        #i32BitCounter := 0;                    // azzera il numero di bit perché non valido
	        RETURN;
	    END_IF;
	    
	    IF #Tst <> 0 THEN
	        #Tst := SHR(IN:=#Tst, N:=1);            // controlla se il byte non è zero lo shifta e incrementa il contatore
	        #i32BitCounter := #i32BitCounter + 1;
	    END_IF;
	    #u1Addr := #u1Addr + 8;                     // incremento dei puntatori
	    #pAnyIN.ByteAddressMSB := #u1.ByteAddrMSB;
	    #pAnyIN.ByteAddressLSB := #u1.WordAddr;
	END_WHILE;
	
	#BitCounter := 0;                               // NO errore di parametro DB
END_FUNCTION

Un'altro esempio di utilizzo potrebbe essere la gestione di bit diversamente posizionati in una word o byte. A tal proposito guarda il prossimo blocco che ho fatto tempo addietro per capirne il funzionamento. Non l'ho mai utilizzato realmente su un impianto ma mi sono divertito a farlo......

 

FUNCTION "SpecchiareBit" : Void
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : pigroplc
VERSION : 0.1
   VAR_INPUT 
      MyByteInput : Byte;
   END_VAR

   VAR_OUTPUT 
      MyByteOutput : Byte;
   END_VAR

   VAR_TEMP 
      MyByteInput_interno : Byte;
      ProvareInput AT MyByteInput_interno : Array[0..7] of Bool;
      MyByteOutput_interno : Byte;
      ProvareOutput AT MyByteOutput_interno : Array[0..7] of Bool;
   END_VAR


BEGIN
	(*
	Esempio per capire come funziona la sovrapposizione variabili con AT 
	Nell'esempio viene specchiato il byte di ingresso nel byte di uscita attribuendo i bit uno a uno
	La funzione AT è possibile nella FC solamente se non è flaggato l'accesso ottimizzato al blocco
	*)
	
	(*
	copiare il byte in parametro interno perché altrimenti non funziona AT negli ingressi
	*)
	#MyByteInput_interno := #MyByteInput;
	
	(*
	specchia i bit uno a uno. Notare che i bit "ProvareInput[x]" NON sono stati modificati direttamente ma sono modificati
	mediante i bit dell'array sovrapposto
	*)
	#ProvareOutput[0] := #ProvareInput[7];
	#ProvareOutput[1] := #ProvareInput[6];
	#ProvareOutput[2] := #ProvareInput[5];
	#ProvareOutput[3] := #ProvareInput[4];
	#ProvareOutput[4] := #ProvareInput[3];
	#ProvareOutput[5] := #ProvareInput[2];
	#ProvareOutput[6] := #ProvareInput[1];
	#ProvareOutput[7] := #ProvareInput[0];
	
	
	(*
	copiare il byte interno nel parametro di uscita. Notare che il parametro "MyByteOutput_interno" NON è stato modificato direttamente
	ma è stato modificato mediante i bit dell'array sovrapposto 
	*)
	#MyByteOutput := #MyByteOutput_interno;
	
END_FUNCTION

 

Modificato: da pigroplc
Link al commento
Condividi su altri siti

Cesare Nicola

OK, grazie toni_unitn e grazie pigro, tengo presente e vediamo se nel progetto che dovrò affrontare salterà fuori l'occasione di utilizzarla.
 

22 ore fa, pigroplc scrisse:

è un po' che non ci sentiamo, 

E' vero. Ti faccio il riassunto delle attività a cui mi sono dedicato dall'ultima volta che ci siamo sentiti:

  1. lavoro
  2. bambini

Immagino non si discostino molto dalle tue.

 

😂😂😂😂😂

Link al commento
Condividi su altri siti

56 minuti fa, Cesare Nicola scrisse:

vediamo se nel progetto che dovrò affrontare salterà fuori l'occasione di utilizzarla.

Io con il contabit faccio lampeggiare la torretta degli allarmi attivi, in precedenza utilizzavo il codice di Batta fatto a suo tempo con lo Step7. 

Per un altro cliente ho gestito facilmente la funzione che accende la sirena al comparire di un allarme non tacitato. In sé la funzione è banale, ho una doppia bitmap, una di allarmi attivi e l'altra di allarmi tacitati. Confronto byte a byte e se almeno uno è differente accendo la sirena. Col fronte di salita della tacitazione faccio l'OR delle aree quindi al ciclo macchina successivo saranno uguali.

Per default invece faccio l'AND qui se un allarme scompare, scompare per entrambe le aree.

 

1 ora fa, Cesare Nicola scrisse:

Ti faccio il riassunto delle attività

idem, a parte che i miei sono meno "bambini" dei tuoi .....

Link al commento
Condividi su altri siti

Cesare Nicola
7 minuti fa, pigroplc scrisse:

Io con il contabit faccio lampeggiare la torretta degli allarmi attivi

Io il contabit lo faccio così (il codice è in ST di Mitsubishi, non trovo in questo momento quello in SCL). Il codice va messo in una funzione da richiamare dove serve (FUN per Mitsubishi, FC per Siemens)

 

(*
DESCRIZIONE

Questa funzione fornisce come valore di ritorno il numero di allarmi attivi.
Gli allarmi a bit devono essere scritti in un array che viene passato come parametro di ingresso alla funzione.
E' possibile definire quanti allarmi controllare. Ad esempio, se il parametro IN_primo è impostato a 10 ed il parametro IN_ultimo a 30,
vengono controllati solo gli allarmi da 10 a 30.

ULTIMA MODIFICA

*)


FUN_conteggio_allarmi := 0;
FOR _i := IN_primo TO IN_ultimo DO
    FUN_conteggio_allarmi := BOOL_TO_INT(IN_array[_i]) + FUN_conteggio_allarmi;
END_FOR;

 

Queste sono le variabili in gioco:

Conteggio.png.aff5d064ae641d5d7fc032707cb492a2.png

Link al commento
Condividi su altri siti

per verificare gli allarmi attivi o meglio per verificare che non ci siano nuovi allarmi e quindi attivare la segnalazione a torretta io faccio un caricamento in dei registri dedicati dei bit allarmi attivi al momento del reset generale allarmi o acquisizione allarmi, poi faccio sempre in continuo un EXOR tra bit allarmi attuali e il registro in cui ho caricato al momento del reset, se dal risultato dell EXOR risulta un bit o più bit attivi (cioè diverso da 0) è il segnale che è arrivato un nuovo allarme da quando ho fatto l'ultimo e reset e perciò faccio attivare la segnalazione di presenza nuovo allarme. Più difficile a spiegarsi che a farsi

Link al commento
Condividi su altri siti

3 ore fa, Cesare Nicola scrisse:

(il codice è in ST di Mitsubishi

oramai hai gli occhi a mandorla, una volta eri fan della famosa marca tedesca ... ah ah ah 

Link al commento
Condividi su altri siti

Cesare Nicola
1 ora fa, pigroplc scrisse:

oramai hai gli occhi a mandorla, una volta eri fan della famosa marca tedesca ... ah ah ah

E' solo opportunismo, che poi è il vero sport nazionale! Prima ero milanista, ma da quando il figlio si dichiara juventino, lo sono un po' anch'io! 😂😂😂
Comunque inizierò oggi un progetto con tutto Siemens; ogni tanto c'è bisogno di trovare una amante, ma poi si torna sempre dalla moglie. 😉

Modificato: da Cesare Nicola
Link al commento
Condividi su altri siti

  • 3 weeks later...
21 ore fa, walterword scrisse:

un milanista che diventa juventino o peggio ancora interista è da ammazzare 

;)

Ho detto "un po' juventino" 😀 Guarda, in realtà, l'essere poco tifoso come me è una situazione privilegiata, che ha solo vantaggi: ti guardi le partite, quella che vuoi, godendoti lo spettacolo e senza che ti vengano acidità di stomaco. Da provare, fidati! 🙂

😂😂🤣
Fine dell'off-topic, mi scuso.

 

Link al commento
Condividi su altri siti

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 account

Accedi

Hai già un account? Accedi qui.

Accedi ora
×
×
  • Crea nuovo/a...