Sergio Bertana
Risposte nei forum create
-
AutorePost
-
Sergio Bertana
Amministratore del forumIn effetti LogicLab permette di definire la copia di variabili sia di tipo array che di tipo struttura, ma non permette operazioni di confronto.
Nelle nostre librerie è disponibile la funzione MemCompare che oltre ad eseguire la comparazione tra qualsiasi tipo di variabile se il parametro Update è attivo esegue anche la copia del valore della variabile definita in Source nella variabile definita in Destination.
Quindi il tuo programma diventerà:
VAR ChkOk : BOOL; MyStruct1 : MYSTRUCT; MyStruct2 : MYSTRUCT; END_VAR MyStruct2:=MyStruct1; ChkOk:=NOT(MemCompare(ADR(MyStruct2), ADR(MyStruct1), SIZEOF(MyStruct2), FALSE));
Maggio 13, 2025 alle 10:16 am in risposta a: Acquisizione in corrente 4-20mA su ingressi analogici modulo CPU #82216Sergio Bertana
Amministratore del forumSu qualsiasi sistema gli ingressi in mA in realtà sono in volt in quanto i convertitori A/D convertono segnali in volt. Quindi per acquisire una corrente si utilizza una resistenza di shunt di valore predefinito e si acquisisce la tensione ai sui capi rilevando la corrente con la legge di Ohm (I=V/R).
Sui nostri moduli CPU SlimLine gli ingressi sono 0-10Volt quindi inserendo una resistenza di shunt da 500Ohm avremo alla corrente di 20mA i 10Volt in ingresso. Se guardi alla definizione AI_MODE del parametro Mode del FB SysGetAnInp vedrai che le acquisizioni in corrente non sono possibili sui moduli CPU. E se le imposti nel programma il FB SysGetAnInp avrà l’uscita di Fault sempre attiva. Con il comando SysLog vedrai il messaggio: User program error:9983200 e dalla pagina errori:
9983200 Il modo acquisizione definito in “Mode” non è corretto SysGetAnInp.
Venendo alla tua necessità di acquisire un trasduttore 4-20mA con gli ingressi analogici del modulo CPU dovrai mettere tra l’ingresso e massa una resistenza da 500Ohm ed acquisire l’ingresso in tensione 0-10Volt. Potrai con la funzione ValueScale trasformare i 2-10Volt acquisiti dall’ingresso nella grandezza ingegneristica rilevata dal trasduttore.
Sergio Bertana
Amministratore del forumLe versioni dei firmware sui moduli sono aggiornate…
Ma riguardando il tuo programma mi sono accorto che nella task Slow esegui una ventina di acquisizioni analogiche (FB SysGetAnInp), come riportato nel capitolo Multiplexing acquisizione del FB l’acquisizione analogica sul bus di estensione richiede un tempo variabile tra i 300-500uS quindi di media occorrono circa 8mS per acquisire tutti i moduli, considerando che il task Slow viene seguito ogni 10mS re dovendo anche gestrire sia gli I/O logici che le uscite PWM si rischia di saturare completamente il tempo e non lasciare più tempo per eseguire la task Back.
Quindi il consiglio è di sequenziare le acquisizioni analogiche tanto è inutile eseguirle ogni 10mS visto che il tempo di conversione A/D è di minimo 60mS e che quando si acquisiscono temperature le variazioni sono molto lente. Alcune informazioni aggiuntive:
- Mi sono perso l’inizio della storia, questo programma inizialmente funzionava e poi ha smesso di funzionare, oppure si hanno sempre avuti problemi fin dall’inizio.
- Le uscite PWM che hanno problemi sono sempre e solo quelle del modulo di estensione oppure sono anche quelle del modulo CPU.
- Puoi inviare al supporto tecnico il file di log del sistema. Lo trovi nella cartella C:\System e lo puoi scaricare via FTP.
- Puoi verificare i tempi di esecuzione delle varie tasks. Puoi mettere in debug la variabili con i tempi presenti in libreria.
Sergio Bertana
Amministratore del forumHo analizzato il programma che hai mandato è molto complesso quindi mi è difficile capire cosa succede ma ho notato alcune cose:
Hai eseguito in task Fast il programma CYCLE che gestisce l’abilitazione delle tue FB di gestione PWM le quali però sono eseguite in task Slow. Questa soluzione non dà vantaggi perchè i tempi di gestione del PWM sono comunque quelli della task Slow. Inoltre gestire variabili cross task con la task Fast può nascondere delle insidie la task Fast non gestisce l’immagine di processo sulle variabili. Quindi il primo consiglio è di spostare il programma CYCLE in task Slow.
Puoi dirmi la versione del firmware del modulo CPU e la versione del firmware del modulo di I/O che gestisce il PWM. Per visualizzare la versione devi connetterti in telnet ed utilizzare il comando SLineInfos.
Quando dici “Talvolta almeno una delle uscite PWM non si ferma anche se Frequency e Duty sono a zero” intendi che non si ferma immediatamente oppure che continua a generare l’uscita.
Quando dici “Talvolta almeno una delle uscite PWM non parte anche se Frequency e Duty sono diversi da zero” in questo caso il valore di Frequency è comunque maggiore del valore minimo gestito da quel canale PWM. I valori li trovi qui nel capitolo “Moduli supportati”.
Per completezza e rifacendomi al cast delle variabili ho visto che la compilazione del tuo programma genera 260 warnings. Certo le warnings solitamente non generano problemi ma attenzione warning come questa
P_OB1(58) - warning G0082: DUTY_C_M3 => Writing a signed value into an unsigned variable
Satnno ad indicare che stai copiando il valore di una variabile con segno nel tuo caso di tipo INT in una variabile senza segno nel tuo caso UINT. Ora se il valore della variabile INT diventa negativo (Ad esempio -1) il valore della variabile UINT divente 65535. Quindi in questi casi bisogna essere certi che la variabile con segno non diventi mai negativa.
Maggio 8, 2025 alle 10:09 am in risposta a: Computo ore funzionamento dispositivi con FB TimeBalancing #82161Sergio Bertana
Amministratore del forumQuindi con selettore in AUT dovrai abilitare il relativo bit in DEnable così da permettere al FB di gestire la pompa in base alla richiesta.
Con il selettore in MAN dovrai disabilitare il relativo bit in DEnable così da impedire al FB di gestire la pompa. E poi dovrai gestire l’incremento del tempo utilizzando l’ingresso che indica lo stato di contatore attivo.
Sergio Bertana
Amministratore del forumLa soluzione è facilmente realizzabile utilizzando il FB HTTPClient. Ti posto il programma HTTPLIAcquire che fà quello che ti serve.
PROGRAM HTTPLIAcquire VAR IRequest : BOOL; (* Input request *) CaseNr : USINT; (* Program case *) Errors : UDINT; (* Execution errors *) APtr : PVOID; (* Auxiliary pointer *) Page : STRING[ 512 ]; (* Page string *) LInput : ARRAY[0..1] OF BOOL; (* Input status *) TCPClient : SysTCPClient; (* TCP client management *) HTTPRq : HTTPClient_v5; (* HTTP client *) END_VAR // ***************************************************************************** // PROGRAM "HTTPLIAcquire" // ***************************************************************************** // Acquisizione ingressi via HTTP. // ----------------------------------------------------------------------------- // ------------------------------------------------------------------------- // INITIALIZATIONS // ------------------------------------------------------------------------- // Program initializations. IF (SysFirstLoop) THEN // Set TCPClient parameters. TCPClient.PeerAdd:=ADR('127.0.0.1'); //Peer address TCPClient.PeerPort:=80; //Peer port TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address TCPClient.LocalPort:=0; //Local port TCPClient.FlushTm:=50; //Flush time (mS) TCPClient.LifeTm:=20; //Life time (S) TCPClient.RxSize:=512; //Rx buffer size TCPClient.TxSize:=512; //Tx buffer size // Set HTTPClient parameters. HTTPRq.SpyOn:=TRUE; //Activate the spy HTTPRq.KeepAlive:=FALSE; //HTTP keep-alive HTTPRq.RMethod:=HTTP_REQUEST#HTTP_GET; //HTTP request method HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname HTTPRq.Page:=ADR('InputRead.html'); //Web page HTTPRq.Request:=eNULL; //Request string HTTPRq.Header:=eNULL; //HTTP header HTTPRq.DBSize:=512; //Data buffer size HTTPRq.Timeout:=T#10s; //Execution timeout END_IF; // ------------------------------------------------------------------------- // MANAGE CONNECTION // ------------------------------------------------------------------------- // Manage the connection. TCPClient(Connect:=HTTPRq.Connect); //TCPClient management HTTPRq(Enable:=TRUE, File:=TCPClient.File); //HTTP client IF (HTTPRq.Fault) THEN CaseNr:=0; END_IF; // ------------------------------------------------------------------------- // PROGRAM SEQUENCIES // ------------------------------------------------------------------------- // Program sequencies. CASE (CaseNr) OF // --------------------------------------------------------------------- // Initialize the time to manage acquisition delay. 0: HTTPRq.Send:=FALSE; //Send request IF NOT(IRequest) THEN RETURN; END_IF; eTO_JUNK(Sysmemset(ADR(Page), 0, SIZEOF(Page))); //Empty page string IRequest:=FALSE; //Input request HTTPRq.Send:=TRUE; //Send request CaseNr:=CaseNr+1; //Program case // --------------------------------------------------------------------- // Trasferisco in "Page" i dati ricevuti. 1: IF ((HTTPRq.DBChars <> 0) AND HTTPRq.HPSelector) THEN IF ((Sysstrlen(ADR(Page))+HTTPRq.DBChars) < SIZEOF(Page)) THEN eTO_JUNK(Sysmemmove(TO_DWORD(ADR(Page))+Sysstrlen(ADR(Page)), HTTPRq.DBAddress, HTTPRq.DBChars)); END_IF; END_IF; // Sul "Done" terminato ricezione risposta. // Controllo se ricevuto dati dal server. IF NOT(HTTPRq.Done) THEN RETURN; END_IF; CaseNr:=0; //Program case IF NOT(HTTPRq.PLoad) THEN Errors:=Errors+1; RETURN; END_IF; // Sul "PLoad" in "Page" ho la stringa ricevuta "&0&0&2&1&1&". // Decodifica valori ricevuti, qui posso fare tutti i controlli. // Intanto se s tringa sempre stessa lunghezza la posso verificare. IF (Sysstrlen(ADR(Page)) <> 11) THEN Errors:=Errors+1; RETURN; END_IF; // Se stringa prima del 1° dato è sempre "&0&0&2&" per leggere il dato. APtr:=SysStrFind(ADR(Page), ADR('&0&0&2&'), FIND_GET_END); IF (APtr = eNULL) THEN Errors:=Errors+1; RETURN; END_IF; IF NOT(SysVsscanf(APtr, ADR('%d'), BOOL_TYPE, ADR(LInput[0]))) THEN Errors:=Errors+1; RETURN; END_IF; IF NOT(SysVsscanf(APtr+2, ADR('%d'), BOOL_TYPE, ADR(LInput[1]))) THEN Errors:=Errors+1; RETURN; END_IF; END_CASE; // [End of file]
Per provarlo ho caricato una pagina con la stringa dfi testo direttamente nella cartella Web dello SlimLine e quindi mi sono connesso al localhost 127.0.0.1. Nel tuo programma dovrai modificare:
TCPClient.PeerAdd:=ADR('192.168.100.101'); //Peer address HTTPRq.Page:=ADR('input.cgi#'); //Web page
Sergio Bertana
Amministratore del forumAdesso è più chiaro…
Si puoi utilizzare MPS056B320 se abbini un convertitore RS232/RS485 come l’HWPATC106. Questo convertitore non isola la RS485 ma se la connessione è interno quadro è accettabile.
Il modulo CPU può gestire 2 uscite PWM se vuoi gestire anche il posizionamento devi connettere le uscite PWM ad un ingresso counter ed il modulo ne supporta 2 nativi e altri 2 con una opzione. Quindi 2 motori passo-passo con frequenza fino a 3KHz sono gestibili con il solo modulo CPU senza opzioni.
Per gestire 4 motori devi utilizzare un modulo di estensione PCB124B010 dove 1 counter è nativo ma serve una opzione per abilitarne altri 3.
Veniamo al FB MotorPositioning, questo FB è stato realizzato per un cliente specifico per il comando del posizionamento via Modbus e prevede l’acquisto di una opzione. Nello svilupparlo abbiamo anche previsto la possibilità di gestire il posizionamento di un motore step con uscita PWM ed ingresso counter ma con il PWM è stata provata solo in laboratorio quindi magari necessita di aggiustamenti in fase di test.
Sergio Bertana
Amministratore del forumCerco di dare le risposte alle varie domande.
- Comando 2 Motori Brushless tramite MODBUS seriale RTU, immagino i driver abbiano interfaccia RS485, quindi ti consiglio MPS054C110. Per la gestione della comunicazione puoi utilizzare il FB ModbusMaster.
- Comando 2 Motori Passo-Passo, puoi utilizzare le 2 uscite digitali del modulo CPU con il FB SysSetPWMOut puoi gestire range frequenza 5Hz÷3kHz.
- 2 Apparecchiature con comando 0-10V, puoi utilizzare un modulo mixed I/O PCB122D100.
- 15 ingressi digitali, 12 sono disponibili sul modulo PCB122D100.
- 10 uscite digitali, 8 sono disponibili sul modulo PCB122D100.
Sergio Bertana
Amministratore del forumNel FB SysSetPWMOut azzerando Frequency e/o azzerando Duty l’uscita PWM deve azzerarsi, così come impostando un valore nei due parametri l’uscita deve generare la frequenza impostata, trovo strano che questo non succeda.
Nel tuo programma vedo che le variabili APPO_FREQ ed APPO_DUTY sono di tipo UINT mentre il FB vuole variabili di tipo REAL, credo che nella compilazione del programma ti vengano segnalate warnings ad indicare questa incongruità di tipo sulle variabili.
Teoricamente LogicLab dovrebbe effettuare il cast del valore corettamente, comunque proverei ad inserire il cast sui valori.
FB_PWM_OUT(Address:=ADDR, Channel:=CHAN, Frequency:=TO_REAL(APPO_FREQ), Duty:=TO_REAL(APPO_DUTY));
- Il FB da te realizzato è sempre eseguito?
- In che task lo stai eseguendo?
- Puoi verificare con il comando SysLog se hai errori di esecuzione?
- Eventualmente puoi inviare il programma alla nostra eMail di supporto e proviamo a dare una occhiata.
Sergio Bertana
Amministratore del forumStrano comportamento, è tutto corretto anche i ponticelli sono corretti.
I ponticelli degli ingressi non utilizzati li puoi lasciare inseriti.
Hai provato ad eseguire solo il programma di acquisizione escludendo tutti gli altri?
Hai provato ad abilitare i controlli Run-time?
Sergio Bertana
Amministratore del forumQuello che succede è molto strano, vediamo alcuni consigli…
Il più importante di tutti è lo stato dei ponticelli, hai inserito LK4 e rimosso LK5 e LK6. Inoltre và rimossa la coppia di ponticelli per ogni ingresso analogico a cui connetti la termocoppia.
Non dici in che task PLC viene eseguito il tuo programma, meglio è eseguirlo in task Back.
Nel programma sposterei il controllo su variabile i prima di eseguire la GetAnInput come qui sotto. Perchè nel tuo programma quando arrivi dopo l’inizializzazione avrai i che punta la posizione dell’array successiva al numero definito andando così fuori array.
i := (i+1); IF i > AN_INPUT_MAX_IDX THEN i := 0; END_IF; FB_GetAnInput[i]();
Ho visto che iteri su tutti i canali quindi anche sul canale 4 che quando viene acquisita almeno una termocoppia non può essere utilizzato. Quindi sicuramente gestendolo con la GetAnInput avrai un errore 9983200 sul canale.
Proverei ad eseguire il solo programma di acquisizione termocoppie escludendo tutti gli altri, per evitare che errori di accesso ad array e/o pointer possano agire sulle variabili del programma di acquisizione “sporcandole”. Dai una occhiata al capitolo Abilitare i controlli run-time programma nelle FAQ.
Sergio Bertana
Amministratore del forumI partially correct what I said in my previous post. Even if in the the LogicLab there isn’t any NEW instruction It’s possible to bypass the obstacle by using the following approach. In the example the FB BlinkValue is instantiated on a SysMAlloc memory.
VAR Blk : BlinkValue; PBlk : ARRAY[0..3] OF @BlinkValue; END_VAR // Allocates memory to instantiate FBs. IF (SysFirstLoop) THEN PBlk[0]:=SysMAlloc(SIZEOF(Blk)); PBlk[1]:=SysMAlloc(SIZEOF(Blk)); PBlk[2]:=SysMAlloc(SIZEOF(Blk)); END_IF; // FBs executing. @PBlk[0].Enable:=TRUE; @PBlk[0].BlinkNr:=1; @PBlk[0](); @PBlk[1].Enable:=TRUE; @PBlk[1].BlinkNr:=2; @PBlk[1](); @PBlk[2].Enable:=TRUE; @PBlk[2].BlinkNr:=3; @PBlk[2]();
As you see to get the size of the FB it’s necessary to instantiate it on a data memory, after that the SIZEOF operator can return its size.
Maggio 5, 2025 alle 4:10 pm in risposta a: Computo ore funzionamento dispositivi con FB TimeBalancing #81896Sergio Bertana
Amministratore del forumAggiungo due esempi di programma per l’aggiornamento del tempo di lavoro dispositivi.
Il primo esempio è più intuitivo ma “paga” l’errore del campionamento sul tempo di loop. In pratica ad ogni secondo si perde il tempo che trascorre tra il passaggio del secondo e quando il programma lo controlla
VAR DOn : ARRAY[0..2] OF BOOL; (* Device attivo *) TTrig : R_TRIG; (* Time trigger *) END_VAR TTrig(CLK:=SysClock1000); //Time trigger IF NOT(TTrig.Q) THEN RETURN; END_IF; // Calcolo tempo dispositivi. IF (DOn[0]) THEN DWTime[0]:=DWTime[0]+1; END_IF; IF (DOn[1]) THEN DWTime[1]:=DWTime[1]+1; END_IF; IF (DOn[2]) THEN DWTime[2]:=DWTime[2]+1; END_IF;
Il secondo esempio calcola il tempo senza errori.
VAR DOn : ARRAY[0..2] OF BOOL; (* Device attivo *) SysTimeMs : UDINT; (* System time (mS) *) TimeDelay : UDINT; (* Time delay (mS) *) TimeBf : UDINT; (* Time buffer (mS) *) END_VAR // Eseguo inizializzazione. IF (SysFirstLoop) THEN TimeBf:=SysTimeGetMs(); //Time buffer (mS) END_IF; // Gestione base tempi. SysTimeMs:=SysTimeGetMs(); //System time (mS) TimeDelay:=SysTimeMs-TimeBf; //Time delay (mS) IF (TimeDelay < 1000) THEN RETURN; END_IF; TimeBf:=SysTimeMs-(TimeDelay-1000); //Time buffer (mS) // Calcolo tempo dispositivi. IF (DOn[0]) THEN DWTime[0]:=DWTime[0]+1; END_IF; IF (DOn[1]) THEN DWTime[1]:=DWTime[1]+1; END_IF; IF (DOn[2]) THEN DWTime[2]:=DWTime[2]+1; END_IF;
Maggio 5, 2025 alle 2:37 pm in risposta a: Computo ore funzionamento dispositivi con FB TimeBalancing #81865Sergio Bertana
Amministratore del forumLa funzione principale del FB TimeBalancing è il comando di dispositivi in base alle ore di funzionamento, settando il numero di bits in DRequest in base al numero di dispositivi da comandare il FB comanda i dispositivi con meno ore attivando i relativi bits in DCommand.
Ma settando i bits in DForce è possibile comandare il relativo dispositivo, indipendentemente dalle sue ore di funzionamento. Alla attivazione di un bit in DForce se il relativo bit in DEnable è attivo il relativo bit in DCommand si attiva. In questo modo è possibile gestire il comando manuale dei dispositivi. Le ore di funzionamento del dispositivo sono conteggiate anche sul comando manuale.
Quindi venendo alla tua necesità, non vedo perchè tu debba utilizzare ulteriori ingressi logici per leggere lo stato del contatore, quando puoi semplicemente gestire i bits in DForce per il comando delle pompe in manuale.
Ma se proprio vuoi prenderti questa “rogna” ti basterà realizzare un programma che sull’attivazione del relativo ingresso logico ad ogni secondo esegue l’incremento del valore del tempo nell’array definito in DWTime alla posizione relativa al dispositivo.
Sergio Bertana
Amministratore del forumOn LogicLab programmable systems we provide two ways to allocate dynamic memory, in the allocated memory buffer it’s possible to store any kind of data but it’s not possible to instantiate FBs. The LogicLab compiler istantiates FBs only in data memory area.
SysMAlloc: This function allocate static dynamic memory and returns its address. There’s a dedicated RAM area on wich the function operates, this area is cleaned at program starting, the function reserves the defined size of memory and returns its address. The allocated area cannot be removed, it still allocated until a the program stops.
SysRMAlloc: This function allocate relocatable dynamic memory and returns its address on a pointer variable. Calling the function if the desired space of memory is available it’s cleaned and the address is saved in the defined buffer. By calling SysRMFree function the buffer is released so the memory can be reused. A proprietary garbage collector operates on the memory moving allocated buffers recovering free spaces optimizing memory use. For this reason the returned address can change during program execution and it’s necessary refer to it anytime before to use it.
So you cannot store FBs on relocatable memory, but if your own FBs use large amount of data for store variable or strings you can use SysRMalloc to create space for them on relocatable memory and freee the space after use. Many of our FBs use this technique to save memory, for example FIFOFile can be set to use dynamic memory to store the file. Since the file on the memory must be used for ever the SysRMalloc is used to allocate it.
In the ST_SendFileByEMail example program the SysRMAlloc function is used to allocate space to save the email body, when the eMail is sent the used memory can be relased.
-
AutorePost