Vai al contenuto
PLC Forum


CJ2M CPU23 & Scheduled interrupt


dott.cicala

Messaggi consigliati

Dovrei leggere una posizione da un encoder SSI tramite la cts21 settata a 400kHz ed eseguire 8 confronti  per poi attivare o meno 8 uscite.

In pratica una specie di modulo camme.

 

 

Quello che mi rende perplesso è che il tutto deve avvenire in 2ms massimi, essere ripetibile e preciso.

 

Usando uno scheduled interrupt che richiama il task(int2) ogni 0.2ms 

 

image.png.3aa2cd54924168ca5805af3b70ca2f7a.png

 

considerando le poche righe che servirebbero e le prestazioni delle uscite OD211

 

image.png.5f8f13e904889afc57366587a0ca06aa.png

 

secondo voi ce la faccio? :blink:

 

Link al commento
Condividi su altri siti


ciao

ho provato in passato , ma il task richiamato non deve essere lungo altrimenti il tempo di scansione si adegua.

i dati messi su datasheet sono teorici. 

Se l'encoder gira lento ok ma se gira per esempio a 500 giri/min o più non è preciso , esperienza provata e testata , poi dipende cosa intendi per ripetibile e preciso.  

Link al commento
Condividi su altri siti

Il mio task esegue solo questo

(* CALCOLO COEFFICIENTI CONVERSIONE POSIZIONE *)
IF NOT clc_ok THEN
	K:=((INT_TO_REAL(CorsaGiro) )/(INT_TO_REAL(ImplusiGiro)))* 10,0;
	clc_ok:=TRUE;
END_IF;

(* POSIZIONE ATTUALE *)
	ACTUAL_POS:=(REAL_TO_INT((INT_TO_REAL( ENCODER)*K))) +OFFSET;   
	iPOSITION:=ACTUAL_POS;

(* CALCOLO VELOCITA' *)
	CNT:=CNT+1;
	CNT:=CNT MOD SWEEP;
	
	IF CNT=0 THEN
		BUFFER:=ACTUAL_POS;
	ELSIF CNT=(SWEEP-1) THEN
		ACT_VEL:=(ABS((ACTUAL_POS-BUFFER1)*100))/18;				
	END_IF;
	
(*SPAZIO PERCORSO PER ANTICIPO*)
	Sp1:=(ACT_VEL*tANTICIPO)/1000;

(* GENERO CAMME *)
inPos[0]:= (ACTUAL_POS>= (START1[0]-Sp1)AND ACTUAL_POS<=(STOP1[0]-Sp1))	OR (ACTUAL_POS>= (START2[0]-Sp1) AND ACTUAL_POS<=(STOP2[0]-Sp1));
inPos[1]:= (ACTUAL_POS>= (START1[1]-Sp1)AND ACTUAL_POS<=(STOP1[1]-Sp1))	OR (ACTUAL_POS>= (START2[1]-Sp1) AND ACTUAL_POS<=(STOP2[1]-Sp1));
inPos[2]:= (ACTUAL_POS>= (START1[2]-Sp1)AND ACTUAL_POS<=(STOP1[2]-Sp1)) OR (ACTUAL_POS>= (START2[2]-Sp1) AND ACTUAL_POS<=(STOP2[2]-Sp1));
inPos[3]:= (ACTUAL_POS>= (START1[3]-Sp1)AND ACTUAL_POS<=(STOP1[3]-Sp1)) OR (ACTUAL_POS>= (START2[3]-Sp1) AND ACTUAL_POS<=(STOP2[3]-Sp1));
inPos[4]:= (ACTUAL_POS>= (START1[4]-Sp1)AND ACTUAL_POS<=(STOP1[4]-Sp1))	OR (ACTUAL_POS>= (START2[4]-Sp1) AND ACTUAL_POS<=(STOP2[4]-Sp1));
inPos[5]:= (ACTUAL_POS>= (START1[5]-Sp1)AND ACTUAL_POS<=(STOP1[5]-Sp1)) OR (ACTUAL_POS>= (START2[5]-Sp1) AND ACTUAL_POS<=(STOP2[5]-Sp1));
inPos[6]:= (ACTUAL_POS>= (START1[6]-Sp1)AND ACTUAL_POS<=(STOP1[6]-Sp1)) OR (ACTUAL_POS>= (START2[6]-Sp1) AND ACTUAL_POS<=(STOP2[6]-Sp1));
inPos[7]:= (ACTUAL_POS>= (START1[7]-Sp1)AND ACTUAL_POS<=(STOP1[7]-Sp1))	OR (ACTUAL_POS>= (START2[7]-Sp1) AND ACTUAL_POS<=(STOP2[7]-Sp1));

(* COMANDO USCITE *)
OUT1:= ENABLE AND ((Ts_ENABLE AND Ts_C1) OR inPos[0]);
OUT2:= ENABLE AND ((Ts_ENABLE AND Ts_C2) OR inPos[1]);
OUT3:= ENABLE AND ((Ts_ENABLE AND Ts_C3) OR inPos[2]);
OUT4:= ENABLE AND ((Ts_ENABLE AND Ts_C4) OR inPos[3]);
OUT5:= ENABLE AND ((Ts_ENABLE AND Ts_C5) OR inPos[4]);
OUT6:= ENABLE AND ((Ts_ENABLE AND Ts_C6) OR inPos[5]);
OUT7:= ENABLE AND ((Ts_ENABLE AND Ts_C7) OR inPos[6]);
OUT8:= ENABLE AND ((Ts_ENABLE AND Ts_C8) OR inPos[7]);
                                                                                                                                         
	
	
	
	
		



 

 

 

La velocità di rotazione dell'encoder è 60 rpm

 

Link al commento
Condividi su altri siti

Guardando le specifiche da te evidenziate qualcosa non torna. Se il tempo di ON è max 0,1 ms e quello di OFF è max 0,8 ms e hai un IRQ ogni 0,2 ms mi viene banalmente da pensare che il tempo massimo per gestire una transizione ON/OFF è di 1,1 ms (può essere meno ma al limite può essere questo). Ammesso che l' evento sia elaborabile realmente in meno di 0,2 ms resta il fatto che 0,8 ms come tempo di OFF impediscono una corretta gestione delle transizione.

Io imposterei lo scheduler con un tempo di 2 ms (o almeno superiore a 1 ms).

Non so quanto si possa migliorare del codice mostrato, forse si potrebbe tentare di sostituire le conversioni in real_int (e viceversa) utilizzando solo dei long int (non conosco il mondo Omron e non so come vengano chiamati e se esistano) ....

 

Potresti anche leggermente velocizzare l' attivazione delle uscite utilizzando il 'cortocircuito' degli operatori booleani :

(* COMANDO USCITE *)
IF ENABLE THEN
	OUT1:= inPos[0] OR (Ts_ENABLE AND Ts_C1);
	OUT2:= inPos[1] OR (Ts_ENABLE AND Ts_C2);
	OUT3:= inPos[2] OR (Ts_ENABLE AND Ts_C3);
	OUT4:= inPos[3] OR (Ts_ENABLE AND Ts_C4);
	OUT5:= inPos[4] OR (Ts_ENABLE AND Ts_C5);
	OUT6:= inPos[5] OR (Ts_ENABLE AND Ts_C6);
	OUT7:= inPos[6] OR (Ts_ENABLE AND Ts_C7);
	OUT8:= inPos[7] OR (Ts_ENABLE AND Ts_C8);
ELSE
	OUT1:= Always_OFF; 'Bit sempre a zero - verificare l' equivalente Omron
	OUT2:= Always_OFF;
	OUT3:= Always_OFF;
	OUT4:= Always_OFF;
	OUT5:= Always_OFF;
	OUT6:= Always_OFF;
	OUT7:= Always_OFF;
	OUT8:= Always_OFF;
END_IF;

 

Link al commento
Condividi su altri siti

Grazie per i suggerimenti.

 

Effettivamente ho omesso qualche informazione in merito al funzionamento del sistema e alla scelta delle uscite statiche standard piuttosto che veloci, le quali non essendo necessarie, determinano un risparmio non indifferente per il cliente.

 

In pratica, l'evento che devo gestire non è altro che una spruzzata (ovvero otto) la cui durata minima non è mai inferiore ai 50ms.

 

L'encoder, per la corsa effettiva dell'asse di 1000mm, effettua un unico giro, poi ritorna.

Non è necessario inibire l'emissione della camma durante il ritorno.

 

L'asse effettua la sua corsa in 2500ms alla velocità di 400mm/s.

Per 50ms di eccitazione, la spruzzata disegna un tratto di 20mm.

 

Considerando i ritardi di commutazione dell'uscita si ha che:

 

per 0,1 ms necessari per l'eccitazione dell'uscita, l'asse ha percorso 0,04mm

per 0,8 ms necessari alla diseccitazione dell'uscita, l'asse ha percorso 0,32mm

 

I ritardi "meccanici" vengono gestiti manualmente (è una specifica cliente) tramite il parametro anticipo [ms]

 

La precisione richiesta è 1mm.

 

La scelta del task schedulato è ovviamente dovuta al fatto che eseguendo questa funzione nel ciclo libero, la risposta non sarebbe costante nel tempo.

 

Calcolando il tempo di esecuzione di ogni istruzione, il richiamo del blocco funzione ecc ecc, mi risulta che servano meno di 50us per eseguire il tutto.

 

E' realistico?

 

Se così fosse, richiamare il blocco funzione ogni 200us dovrebbe garantire un buon margine.

 

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