SETTANTASETTE Inserito: 30 dicembre 2015 Segnala Share Inserito: 30 dicembre 2015 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-pointA 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 More sharing options...
Livio Orsini Inserita: 30 dicembre 2015 Segnala Share Inserita: 30 dicembre 2015 (modificato) mi devo sorbire della programmazione per PLCTi capisco, chi è abituato a programmare apparati embedded trova il PLC "legnoso" e macchinoso, specie se usi linguaggi laddder diagram come KOP.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: 30 dicembre 2015 da Livio Orsini Link al commento Condividi su altri siti More sharing options...
drugo66 Inserita: 30 dicembre 2015 Segnala Share Inserita: 30 dicembre 2015 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 More sharing options...
batta Inserita: 31 dicembre 2015 Segnala Share Inserita: 31 dicembre 2015 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 More sharing options...
FabioS.PLC Inserita: 6 gennaio 2016 Segnala Share Inserita: 6 gennaio 2016 (modificato) 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, ovveroORGANIZATION_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: 6 gennaio 2016 da FabioS.PLC Link al commento Condividi su altri siti More sharing options...
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