Ciao a tutti, premetto che ho appena iniziato ad interessarmi a questa problematica (quindi non posso dire di saperne più di tanto) e che ho già letto il thread iniziato da Igor riguardo la gestione di un encoder incrementale, ma vorrei avere alcuni chiarimenti o suggerimenti.
Vorrei realizzare un asse, con motore in continua, per una macchina a CN. Ho già realizzato un hardware di prova costituito da un PIC16F628 20MHz, un encoder incrementale a 1000 i/giro (1000 o1024? sull'encoder vedo una sigla dove appare 1000), un driver con 4 mosfet collegati ponte H comandato in PWM per dare potenza ad un motore in continua da 40V a 3000rpm max.
Il mio sistema deve, in pratica, eseguire in controllo PID software di velocità del motore da
0 a 3000rpm.
Ho provato le seguenti soluzioni:
1) BASE DEI TEMPI COSTANTE - Il clock derivante dall'encoder entra nel pin RB6/T1OSO/T1CKI del PIC che incrementa direttamente il registro a 16bit del timer1 configurato come contatore. - Questo registro viene poi memorizzato in altra locazione e resettatosull'interrupt del timer0 interno al PIC che definisce quindi la base dei tempi per il campionamento. - Il contenuto di questo registro contiene quindi un valore proporzionale alla velocità di rotazione del motore senza tenere conto però del verso di rotazione. - Attraverso un FlipFlop D esterno ricavo il verso di rotazione del motore che entra in ingresso al PIC. - Nel Loop principale il PIC rileva il verso di rotazione e sistema il segno del registro precedentemente campionato che entra quindi nell'algoritmo PID che calcola l'uscita di comando del PWM.
2) BASE DEI TEMPI VARIABILE - Il timer1 è, questa volta, è configurato come timer. - Il clock derivante dall'encoder entra nel pin RB0/INT che genera una richiesta di interrupt nella quale non faccio altro che campionare il registro del timer1 per poi resettarlo. - Il contenuto di questo registro contiene quindi un valore inversamente proporzionale alla velocità di rotazione del motore senza tenere conto però del verso di rotazione. - Attraverso un FlipFlop D esterno ricavo il verso di rotazione del motore che entra in ingresso al PIC. - Il segno del registro precedentemente campionato viene modificato per tenere conto del verso di rotazione. - Nel Loop principale il PIC rileva il verso di rotazione, sistema il segno del registro precedentemente campionato ed esegue una divisione tra una costante ed il registro modificato. - Il risultato della divisione contiene quindi un valore proporzionale alla velocità di rotazione del motore che tiene già conto del verso, ed entra quindi nell'algoritmo PID che calcola l'uscita di comando del PWM.
Supponendo che l'encoder sia un 1000i/g avrei una freq. massima su ogni canale di 3000/60*1000 = 50KHz.
Se collego il CHA ed il CHB ad uno XOR ottengo un clock a frequenza doppia rispetto ad ogni singolo canale e se lo porto in ingresso ad un duplicatore di frequenza otterrei la gestione sia dei fronti di salita che di discesa di tale clock. Per le mie prove mi sono "accontentato" del clock generato dallo xor, sia per la prima che per la seconda soluzione, quindi max 100KHz.
LA PRIMA SOLUZIONE mi permette di avere una base dei tempi costante per tutti i regimi di rotazione, che però deve essere tale da permettere al timer 1 di raggiungere un certo conteggio alla massima velocità di rotazione del motore per non andare a scapito della risoluzione del sistema, quindi:
Per ottenere una risoluzione di 1rpm mi serve un conteggio di 3000i del timer 1 a
3000rpm.
Con una freq. in ingresso di 100KHz il timer1 incrementa ogni 10uSec, per cui ottengo un conteggio di 3000i dopo 10u*3000 = 30mSec. Utilizzo quindi questa base dei tempi per configurare il timer0 (in realtà il timer0 a 8bit non raggiunge un tempo cosi' elevato nemmeno utilizzando il prescaler dedicato, quindi nella routine di interrupt del timer0 incremento una mia variabile a 8 bit prima di campionare il timer1).
Come ho premesso non me ne intendo molto, ma credo che una base dei tempi di
30mSec sia un tantino elevata per un controllo di velocità.
LA SECONDA SOLUZIONE, invece, mi permette di avere una base dei tempi variabile al variare del numero dei giri motore che può anche essere più spinta di quella della soluzione 1, senza andare a sfavore della risoluzione del sistema che invece rimane ben più alta. Con una freq. in ingresso di 100KHz il timer1 la routine di servizio del pin RB0/INT viene richiamata ogni 10uSec, quindi ho a disposizione meno di 50 istruzioni per servire l'interrupt (comprese le istruzioni per ripristinare l'accumulatore, il reg. di stato ecc...). Dato che devo solo fermare il timer1, salvare i due reg. del timer da un'altra parte per poi resettarli, quindi far ripartire il timer, credo di farcela anche con meno di 50 istruzioni.
A motore fermo il timer1 va in overflow quindi nella routine di servizio di questo evento fermo il timer ed imposto un valore limite per i registri del timer (supponiamo 65535, poi vediamo xkè).
Nel loop principale, infatti, divido la costante 65500 per il valore del registro a 16bit del timer1 che, a motore fermo vale 65535, ottenendo un numero direttamente proporzionale alla velocità di rotazione del motore. Configurando il prescaler del timer1 con 1:2 per avere un incremento ogni 0.8uSec, alla velocità max. il timer 1 raggiunge un conteggio pari a
10uS/0.8uS = 12.
La divisione della costante 65500 per 12 restituisce 5640. Il risultato della divisione è direttamente proporzionale alla velocità di rotazione del motore ed il valore 5640 corrispondente a 3000 rpm indica che ho una risoluzione di 3000/5640 =0.57rpm.
Con un oscilloscopio collegato ad un'uscita del PIC, che inverto ad ogni ciclo di PID, ho rilevato che l'algoritmo PID esegue tutti i calcoli in meno di 550usec.
Secondo voi ho i miei ragionamenti sono corretti? Quale delle due soluzioni è secondo voi la mogliore? Ci sono altre possibili soluzioni (a basso costo e possibilmente utilizzando un PIC)?
Ho provato ad accendere il tutto e, per fortuna, non è scoppiato niente ;c)
Attualmente so lavorando sulla soluzione 2 : Alzando il guadagno incrementale del PID sento che il motore "resiste" quando provo a ruotare a mano il rotore. Ho provato a dare un riferimento di velocità basso in positivo e poi in negativo, e vedevo il motore che iniziava a ruotare prima in un senso e poi nell'altro. Ho provato a fermare con le mani il rotore ed effettivamente sentivo il motore che spingeva ancora con più forza.
A questo punto avevo già preparato la bottiglia di spumante e i pasticcini per festeggiare ;c) ma quando ho provato ad alzare ancora un po il riferimento di velocità mi sono accorto che il motore andava leggermente a "scatti".
Anche da fermo se alzo un po i guadagni e provo a muovere il rotore il sistema entra in oscillazione. In pratica non riesco a tarare il mio azionamento come vorrei.
Forse ho veramente sbagliato qualche concetto? Qualche calcolo? Forse ho bisogno di una più veloce risposta del sistema? (magari posso usare un PIC a 40MHz.) O forse mi serve più precisione e devo quindi utilizzare sia i fronti di salita che quelli di discesa dei canali encoder?
Penso che questo tema possa essere interessare anche per altri quindi se volete posso inviarvi i sorgenti del firmware che sto utilizzando (soluzione 2). Non ho ancora uno schema pronto del circuito elettrico ma se serve posso prepararlo.
Scusate se mi sono dilungato troppo ma almeno cosi' credo di essere stato chiaro. Grazie in anticipo a tutti.