Vai al contenuto
PLC Forum


Come campionare un Segnale Analogico


SETTANTASETTE

Messaggi consigliati

Mi occupo in prima istanza di Sistemi Embedded a Microcontrollore, ma  due tre volte l'anno mi devo sorbire della programmazione per PLC. Ovviamente non maneggiando continuamente l'argomento, continuo a dimenticarmi anche nozioni elementari, per cui a volte chiedo aiuto per cose banali...

Dunque... per chi ha voglia di rispondere:

Il problema e' l'acquisizione, campionamento, di un segnale analogico per poter operare un confronto con un Set-point.

In C la soluzione e' semplice:

Schiaccio un pulsante che aziona una pompa la quale svuota un serbatoio

- Inizializzo il valore iniziale del serbatoio (es. 600 cm)

Ogni sec un timer genera un interrupt che avvia la funzione di Acquisizione, la quale opera in questo modo:

- Legge il valore del serbatoio

- Sottrae il valore del serbatoio al valore inizializzato-fotografato al premere del pulsante

- Comparazione tra risultato della sottrazione e valore impostato nel set-point

- Se risultato = Set-point: spegni pompa

- Se risultato ancora minore di Set-point: azzera il valore letto-campionato e ripeti l'operazione (solo al prossimo interrupt del Timer)

 

In Kop ho creato due timer che si autoresettano con cadenza di 500msec in modo da avere un output di 1 Hz per il campionamento.
Ho utilizzato i Timer e non le funzioni temporizzate del sistema per avere una flessibilita' di regolazione.

In seconda istanza ho creato un unico lungo segmento che e' attivato dal timer, seguito da un pulsante a fronte P positivo per avere un singolo impulso.

Questo "segmentone" contiene:

- un MOVE il valore letto su una db temporanea - SUB valore Iniziale con valore letto - CMP risultato SUB con Set-point

A questo punto mi sono bloccato perche' mi sfugge il successivo segmento come utilizzare (e temporizzare) il risultato del CMP e prendere le due diramazioni

- Ferma pompa

- Azzera valore temporaneo e ripeti il ciclo.

 

Purtroppo mi sfugge come implementare un semplice goto o un ciclo (while o for ) dove si ripete un numero indefinito  la stessa operazione, senza procedere avanti nella lettura da parte della CPU dei segmenti concatenati.

 

Grazie a chi mi da' due dritte, ma anche a chi legge solamente...

 

 

Link al commento
Condividi su altri siti


mi devo sorbire della programmazione per PLC

Ti capisco, chi è abituato a programmare apparati embedded trova il PLC "legnoso" e macchinoso, specie se usi linguaggi laddder diagram come KOP.:smile::(

Puoi fare il tutto pari pari al "C", magari programmando in AWL che è un qualche cosa simil assembler.

Basta farti un timer di sistema che scatena l'interrupt dell'OB35.

Se fai il timer a 100 ms (che credo sia il valore di default), basta poi fare un contatore per raggiungere l0intervallo di tempo ideale. A contatore scaduto chiami la funzione che acquisisce il valore analogico ed esegue le comparazioni.

Sintassi a parte non c'è differenza alcuna dal fare la funzione in "C".

Modificato: da Livio Orsini
Link al commento
Condividi su altri siti

Ciao,

sia in KOP (mi sembra) che AWL esistono le istruzioni di salto, quindi eseguire un loop o un GOTO non è impossibile, ma a me non piace molto: si sacrifica la leggibilità; quindi perchè non sfruttare appieno le istruzioni del PLC ?

Io farei così: il ciclo si avvia con la pressione del pulsante che avvia la pompa, quindi la pressione del pulsante + (AND) la pompa in marcia attivi un merker che ti indica che sei in ciclo; a questo punto (proprio con quel merker attivo) fai partire il clock ed esegui le tue letture; poi fai il primo confronto (lettura == setpoint fermi la pompa ed il ciclo è terminato - resetti tutte le tue memorie di ciclo) ed il secondo (lettura < setpoint non fermi la pompa, azzeri la temporanea, quindi il ciclo continua); per fermare la pompa basta che resetti il merker che ti avviato la pompa (sono abituato a lavorare con le memorie intermedia di appoggio e, solo in fondo al blocco, usare quelle per pilotare le uscita), oppure puoi settare una memoria che inibisce la pompa; scegli tu.

Non hai menzionato eventuali consensi: anche quelli potrebbero resettare il ciclo avviato e la pompa se mancano le condizioni.

Questo sempre ammesso che io abbia capito bene il tuo ciclo ...

Link al commento
Condividi su altri siti

Il problema e' l'acquisizione, campionamento, di un segnale analogico per poter operare un confronto con un Set-point.

In C la soluzione e' semplice:

Schiaccio un pulsante che aziona una pompa la quale svuota un serbatoio

- Inizializzo il valore iniziale del serbatoio (es. 600 cm)

Ogni sec un timer genera un interrupt che avvia la funzione di Acquisizione, la quale opera in questo modo:

- Legge il valore del serbatoio

- Sottrae il valore del serbatoio al valore inizializzato-fotografato al premere del pulsante

- Comparazione tra risultato della sottrazione e valore impostato nel set-point

- Se risultato = Set-point: spegni pompa

- Se risultato ancora minore di Set-point: azzera il valore letto-campionato e ripeti l'operazione (solo al prossimo interrupt del Timer)

Prima di tutto, non capisco se tu debba solo solo fermare la pompa raggiunto il livello i set-point, quindi acquisizione e confronto, o campionare il segnale.

Voglio dire, per me "campionare" un segnale analogico significa archiviare da qualche parte il suo valore.

Da quanto scrivi invece non vedo questa operazione di memorizzazione del dato ad ogni secondo, quindi non parlerei di "campionamento".

Mi sembra invece che si tratti solo di rilevare la velocità di svuotamento del serbatoio e, se questa velocità raggiunge o supera il valore impostato, si deve spegnere la pompa.

 

Non capisco poi perché utilizzare due timer per creare il clock di un secondo. Basta un solo timer. E non è questione di linguaggio: basta un solo timer sia che si programmi in KOP, in AWL o in SCL.

Se si desidera una precisione maggiore, si possono utilizzare gli interrupt a tempo (per esempio, OB35).

Per il resto, basta creare una funzione (FC) alla quale puoi passare parametri in ingresso, in uscita e in ingresso/uscita, e programmare tutto quello che ti serve in questa funzione (più o meno come faresti col C).

Se sei abituato a programmare in C forse ti conviene scrivere questa funzione in SCL (testo strutturato).

In ogni caso, indipendentemente dal linguaggio utilizzato, sono solo poche e banali operazioni. Non riesco a capire dove sia il problema.

Link al commento
Condividi su altri siti

Anche io come consiglia Batta userei un OB35. Ma se per te è proprio necessario poter modificare facilmente (ad esempio anche in run time) il periodo di esecuzione del Cycle Time Interrupt, potresti usare un Time Delay Interrupt (OB20). Per quanto mi riguarda una cosa così semplice la farei in ladder, ma se hai maggiore familiarità con l'SCL io farei cosi:

 

uso un blocco dati dove salvo alcune variabili che mi serviranno dopo:

DATA_BLOCK "Dati"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   STRUCT 
      livello_iniziale : Int;
      cycle_interrupt : Time;
      ret_val_srt : Int;
      ret_val_can : Int;
      Acquisizione_old : Bool;
   END_STRUCT;


BEGIN

END_DATA_BLOCK

 

Creo una funzione per il controllo della pompa che richiamerò nell'OB20:

FUNCTION "Controllo_pompa" : Void
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   VAR_INPUT 
      Esecuzione : Bool;
      livello_attuale : Int;
      livello_iniziale : Int;
      set_point : Int;
   END_VAR

   VAR_OUTPUT 
      out : Bool;
   END_VAR


BEGIN
    IF #Esecuzione THEN
        IF (#livello_iniziale - #livello_attuale) >= #set_point THEN
            #out := 0;
        ELSE
            #out := 1;
        END_IF;
    ELSE
        #out := 0;
    END_IF;
    
END_FUNCTION

 

Come detto richiamo tale funzione nell'OB20, ovvero

ORGANIZATION_BLOCK "Delay_interrupt"
TITLE = "Time Delay Interrupt"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   VAR_TEMP 
      OB20_EV_CLASS : Byte;   // Bits 0-3 = 1 (Coming event), Bits 4-7 = 1 (Event class 1)
      OB20_STRT_INF : Byte;   // 16#21 (OB 20 has started)
      OB20_PRIORITY : Byte;   // Priority of OB Execution
      OB20_OB_NUMBR : Byte;   // 20 (Organization block 20, OB20)
      OB20_RESERVED_1 : Byte;   // Reserved for system
      OB20_RESERVED_2 : Byte;   // Reserved for system
      OB20_SIGN : Word;   // Identifier input (SIGN) attached to SRT_DINT
      OB20_DTIME : Time;   // Delay time (DTIME) input to SRT_DINT instruction
      OB20_DATE_TIME : Date_And_Time;   // Date and time OB20 started
   END_VAR


BEGIN
    //rieseguo ciclicamente l'OB20 dopo il tempo salvato in "Dati".Cycle_interrupt
    "Dati".ret_val_srt := SRT_DINT(OB_NR := 20, DTIME := "Dati".cycle_interrupt, SIGN := 0);
    

    "Controllo_pompa"(Esecuzione := "Acquisizione",
                      livello_attuale := "livello",
                      livello_iniziale := "Dati".livello_iniziale,
                      set_point := 100,
                      out => "pompa");
    
END_ORGANIZATION_BLOCK

 

E infine il Main :

ORGANIZATION_BLOCK "Main"
TITLE = "Main Program Sweep (Cycle)"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   VAR_TEMP 
      OB1_EV_CLASS : Byte;   // Bits 0-3 = 1 (Coming event), Bits 4-7 = 1 (Event class 1)
      OB1_SCAN_1 : Byte;   // 1 (Cold restart scan 1 of OB 1), 3 (Scan 2-n of OB 1)
      OB1_PRIORITY : Byte;   // Priority of OB Execution
      OB1_OB_NUMBR : Byte;   // 1 (Organization block 1, OB1)
      OB1_RESERVED_1 : Byte;   // Reserved for system
      OB1_RESERVED_2 : Byte;   // Reserved for system
      OB1_PREV_CYCLE : Int;   // Cycle time of previous OB1 scan (milliseconds)
      OB1_MIN_CYCLE : Int;   // Minimum cycle time of OB1 (milliseconds)
      OB1_MAX_CYCLE : Int;   // Maximum cycle time of OB1 (milliseconds)
      OB1_DATE_TIME : Date_And_Time;   // Date and time OB1 started
   END_VAR


BEGIN
    //Scelgo il periodo di esecuzione dell'OB20
	"Dati".cycle_interrupt := T#1s;
	
	IF "stop" THEN
	    "Acquisizione" := 0;
	    "pompa" := 0;
	ELSIF "start" THEN
	    "Acquisizione" := 1;
	END_IF;
	
	//siccome non è presente un R_Trig in SCL per S7-300 allora ne costruisco uno io,
	//ovvero inizializzo il livello iniziale del serbatoio e attivo l'interrupt OB20 solo in 
    //caso di positive Edge detector dell'input "Acquisizione"
	IF "Acquisizione" AND NOT "Dati".Acquisizione_old THEN
	    "Dati".ret_val_srt := SRT_DINT(OB_NR := 20, DTIME := "Dati".cycle_interrupt, SIGN := 0);
	    "Dati".livello_iniziale := "livello";
	END_IF;
	"Dati".Acquisizione_old := "Acquisizione";
	
    //disattivo l'OB20 se è stato premuto il pulsante di Stop o se 
    //la differenza tra il livello iniziale e quello attuale è maggiore o uguale al setpoint
	IF NOT "Acquisizione" THEN
	    "Dati".ret_val_can:=CAN_DINT(20);
	END_IF;
	
	
	    
	    
END_ORGANIZATION_BLOCK

 

Modificato: da FabioS.PLC
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...