Vai al contenuto
PLC Forum


Prelevare Una Word Da Una Variabile Real In Un Fb...


Sperial

Messaggi consigliati

Buongiorno a tutti,

sto cercando di capire come "scambiare tra loro" le due word che compongono un dato reale all'interno di un FB multistanza.

Cerco di spiegarmi anche se sono quasi certo di non riuscirci e per questo mi scuso in anticipo.

Nella dichiarazione delle variabili IN ho 16 variabili reali che partono dall'indirizzo 2.0 (sullo 0.0 è stata messa una variabile bool)

Nella out ho altrettante 16 variabili che partiranno da [2 + (16*4)] 66.0 per altri 64 bytes o giù di li insomma :smile:

Dovrei prendere la WORD 2 e metterla nella WORD 68, poi prendere la WORD 4 e infilarla nella WORD 64.

Il nostro programmatore ha scritto così:

...

L DBW 2
T DBW 68
L DBW 4
T DBW 64
...

ma proprio non ne vuole sapere di funzionare e la CPU mi va in stop con l'errore

Evento 4 di 86: ID di evento 16# 2523
Errore di lunghezza di campo in scrittura
DB globale, accesso a parola, Indirizzo d'accesso: 70
N. FB: 13
Indirizzo blocco: 228
OB richiesto: OB di errore di programmazione (OB 121)
OB assente, inibito o non avviabile nello stato di funzionamento attuale
Errore interno, Evento entrante
13.11.03.464 12/08/2013
L'idea che mi sono fatto è che all'interno di un FB sia più complesso o forse vietato giocare con i pezzi di real richiamandoli come word ma non ne sono sicuro.
Dove stiamo sbagliando?
Grazie per l'attenzione!
Modificato: da Sperial
Link al commento
Condividi su altri siti


Cominciamo col chiarire alcuni dubbi.

Quando parli di "dato reale" e di "variabili reali" intendi dire che sono variabili di tipo REAL o usi il termine "reale" solo per dire che non sono variabili temporanee?

Comunque, l'errore direi che è banale, a meno che non ci siano altre parti di codice che non descrivi nel post.

Con l'istruzione

L DBW 2

carichi la variabile DB" 2 del DB attualmente aperto. Ma qual è il DB attualmente aperto? Se non hai aperto il DB di istanza della funzione con l'istruzione AUF DBxxx, sicuramente il DB aperto è un altro.

E poi aprire il DB di istanza con l'istruzione AUF non sarebbe nemmeno la soluzione migliore.

Per accedere ai dati del DB di istanza dall'interno della funzione, la sintassi da usare è:

L DIW 2

T DIW 68

L DIW 4

T DIW 66 (l'indirizzo è 66, e non 64)

Poi ci sarebbe un modo più elegante di risolvere il problema, che non utilizza gli indirizzi assoluti ma i puntatori. In questo modo, se aggiungi variabili non devi modificare il codice.

E non ti devi preoccupare degli indirizzi assoluti nemmeno se richiami la funzione come multiistanza all'interno di un'altra FB.

Per fare un esempio, ho creato nelle variabili in ingresso e in uscita due array da 16 variabili REAL chiamati rispettivamente "array_IN" e "array_OUT".

Poi ho creato due variabili temporanee di tipo WORD.

Per scambiare i primi due byte con gli ultimi due, potresti scrivere:

      LAR1  P##array_IN
      L     W [AR1,P#0.0]
      T     #tmp_W1
      L     W [AR1,P#2.0]
      T     #tmp_W2

      LAR1  P##array_OUT
      L     #tmp_W1
      T     W [AR1,P#2.0]
      L     #tmp_W2
      T     W [AR1,P#0.0]
Link al commento
Condividi su altri siti

Batta,

ti confermo che mi riferivo al tipo di dati REAL.

Hai proprio ragione, l'errore è banale ma per uno zugnone come il sottoscritto il tuo aiuto è stato indispensabile!

Ora vado a vedermi qual è il significato del termine DIW e poi cerco di capire come funziona l'esempio sugli array che mi hai girato.

Ti ringrazio di cuore, per me si può chiudere qui.

Link al commento
Condividi su altri siti

DI sta per blocco dati di Istanza.

Se sei all'interno di una FB, con DI accedi ai dati del DB di istanza di quella FB.

Per quanto riguarda il codice dell'esempio, ecco una descrizione:

con l'istruzione LAR1 P##array_IN carichi nel registro indirizzi l'indirizzo di inizio della variabile (in questo caso un array).

Per spiegare meglio questa istruzione, possiamo analizzarla scrivendola in un modo equivalente:

L P##array_IN

LAR1

Se al nome di una variabile anteponiamo P# anziché caricare il valore della variabile, andremo a caricare il suo indirizzo.

Con L P##array_IN quindi carichiamo nell'accumulatore 1 l'indirizzo della variabile #array_IN

Poi con l'istruzione LAR1 si trasferisce il contenuto dell'accumulatore 1 nel registro indirizzi 1.

Scrivendo LAR1 P##array_IN si ottiene lo stesso identico risultato con una sola riga anziché due.

Con l'istruzione L W [AR1,P#0.0] vado a caricare nell'accumulatore 1 una variabile da 16 bit con indirizzo corrispondente al registro indirizzi 1 (AR1). In questo caso infatti l'offset è zero (P#0.0).

Con l'istruzione L W [AR1,P#2.0] carico una variabile da 16 bit che ha come indirizzo quello indicato dal registro indirizzi AR1 + 2 byte (P#2.0 = offset di due byte).

Lo stesso discorso vale per la scrittura delle variabili.

Volendo al posto di W [AR1,.....] potresti scrivere DIW [AR1,.....]. L'indicazione "DI" in realtà non serve perché l'area di memoria è già contenuta nell'indirizzo.

Andando a specificare l'area di memoria (in questo caso DI ma potrebbe essere qualsiasi altra cosa), andresti a "forzare" la lettura/scrittura della variabile nell'area di memoria indicata nell'istruzione. L'area di memoria compresa all'interno dell'indirizzo verrebbe ignorata.

Quindi se sei sicuro che le tue operazioni vanno effettuate in una specifica area di memoria, indicare in modo esplicito questa area nell'istruzione può servire a rendere più chiaro il codice (vedo a colpo d'occhio dove vado a leggere/scrivere). Se invece vuoi usare il registro indirizzi in modo più flessibile, meglio non indicare nell'istruzione l'area di memoria. Per dire, il codice dell'esempio va bene anche se gli array sono in due aree di memoria diverse (per esempio uno in un DB di istanza e l'altro in un DB globale o nell'area M), ma solo se non specifichi l'area di memoria nell'istruzione.

Dal tuo post però mi viene da pensare che questa operazione di scambio di word tu la debba fare per tutte le 16 variabili dell'array.

In questo caso potresti procedere in (almeno) due modi: uno semplicemente ripetendo 16 volte il codice e modificando di volta in volta l'offset (quello dopo la virgola all'interno delle parentesi quadre), oppure (cosa che io consiglierei vivamente) utilizzare un loop per far fare in automatico al PLC l'incremento degli indirizzi.

Se ti interessa, giusto per giocare un po' ho fatto una funzione che fa questa operazione. Alla funzione devi passare l'indirizzo della prima variabile da convertire, l'indirizzo della prima variabile convertita, e il numero di variabili da convertire.

Di seguito c'è il sorgente di questa FC. Puoi creare una nuova "sorgente awl" all'interno della cartella "Sorgenti", copiare il codice e compilare.

ATTENZIONE!!! Con la compilazione viene generata FC1. Se esiste già una FC1 nel tuo progetto, questa verrà irrimediabilmente sovrascritta. Modifica la prima riga del sorgente mettendo il numero di una FC che non esiste.

FUNCTION FC 1 : VOID
TITLE =
AUTHOR : batta
VERSION : 0.1


VAR_INPUT
  IN : POINTER ;	
  NrVar : INT ;	
END_VAR
VAR_OUTPUT
  OUT : POINTER ;	
END_VAR
VAR_TEMP
  id : INT ;	
  Adr_IN : DINT ;	
  Adr_OUT : DINT ;	
  DB_IN : INT ;	
  DB_OUT : INT ;	
  tmp_W1 : WORD ;	
  tmp_W2 : WORD ;	
END_VAR
BEGIN
NETWORK
TITLE =

//N.B.: la variabile di tipo Pointer è una variabile da 6 byte che contiene
//il numero del DB (0 = non è un DB), l'area di memoria e l'indirizzo della variabile.
//Per interpretare il contenuto di questo puntatore, vado a caricare nel registro
//indirizzi AR1 l'indirizzo del puntatore stesso. Poi vado a leggere i primi 16 bit
//del puntatore ed estraggo il numero del DB. Gli altri 32 bit (per leggere i quali
//mi sposto di 2 byte indicando l'offset P#2.0) contengono l'indirizzo della variabile,
//completo di area di memoria.
//Il registro indirizzi 1 viene quindi usato in due modi: prima caricando in AR1
//l'indirizzo delle variabili Pointer "IN" e "OUT", poi caricando in AR1 l'indirizzo
//delle variabili da leggere/scrivere.

      L     P##IN; //Carica nel registro indirizzi l'indirizzo della variabile
      LAR1  ; //"Pointer" contenente l'indirizzo dell'array in ingresso

      L     W [AR1,P#0.0]; //Estrai dalla variabile Pointer il numero del DB
      T     #DB_IN; 
      L     D [AR1,P#2.0]; //Estrai dalla variabile Pointer l'indirizzo
      T     #Adr_IN; //di inizio dell'array

      L     P##OUT; //Carica nel registro indirizzi l'indirizzo della variabile
      LAR1  ; //"Pointer" contenente l'indirizzo dell'array in uscita

      L     W [AR1,P#0.0]; //Estrai dalla variabile Pointer il numero del DB
      T     #DB_OUT; 
      L     D [AR1,P#2.0]; //Estrai dalla variabile Pointer l'indirizzo
      T     #Adr_OUT; //di inizio dell'array


      L     #NrVar; 
NEXT: T     #id; //Carica indice del loop con numero di variabili

      L     0; 
      L     #DB_IN; //Controlla se l'array IN è in un DB 
      ==I   ; 
      SPB   M000; 
      AUF   DB [#DB_IN]; //Apri il DB dell'array IN
M000: LAR1  #Adr_IN; 
      L     W [AR1,P#0.0]; //Appoggia la word sinistra della variabile IN
      T     #tmp_W1; //a una variabile temporanea
      L     W [AR1,P#2.0]; //Appoggia la word destra della variabile IN
      T     #tmp_W2; //a una variabile temporanea

      L     0; 
      L     #DB_OUT; //Controlla se l'array OUT è in un DB 
      ==I   ; 
      SPB   M001; 
      AUF   DB [#DB_OUT]; //Apri il DB dell'array OUT
M001: LAR1  #Adr_OUT; 
      L     #tmp_W2; //Scrivi la word destra della variabile IN
      T     W [AR1,P#0.0]; //nella word sinistra della variabile OUT
      L     #tmp_W1; //Scrivi la word sinistra della variabile IN
      T     W [AR1,P#2.0]; //nella word destra della variabile OUT

      L     #Adr_IN; //Incrementa l'indirizzo della variabile IN 
      +     L#32; //di 32 bit (4 byte)
      T     #Adr_IN; 

      L     #Adr_OUT; //Incrementa l'indirizzo della variabile OUT 
      +     L#32; //di 32 bit (4 byte)
      T     #Adr_OUT; 

      L     #id; //Carica indice loop
      LOOP  NEXT; //Controlla se loop è terminato

END_FUNCTION

Poi nella tua FB ti basta richiamare questa FC come segue:

      CALL  FC     1
       IN   :=P##array_IN
       NrVar:=16
       OUT  :=P##array_OUT

Tieni presente che questa FC l'ho fatta in fretta e l'ho provata altrettanto in fretta col simulatore. Io sono sicuro che funziona, ma non mi assumo responsabilità.

Link al commento
Condividi su altri siti

Che dire, grazie mille! mi sono fatto un bel PDF e domani comincio a guardarmelo! Sono in Qatar per lavoro ma causa classici rallentamenti dovuti a cause esterne ho veramente tanto tempo libero!

Il discorso DI e DB adesso mi è chiaro, ti confesso che ho cercato nell'. in linea dello Step 7 e non ho trovato niente, quindi le tue dritte mi sono state nuovamente utili!

Link al commento
Condividi su altri siti

Me la sono letta e credo di averla capita, il discorso dei puntatori mi ha sempre mandato in panico ma ora comincio a farmi un'idea (ancora vaga) di come funzionano.

Grazie ancora, farò qualche prova in questi giorni!

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...