Vai al contenuto
PLC Forum


Problema Contagiri Pic - Maggiore precisione


Edge

Messaggi consigliati

Ciao a tutti.

Vorrei una delucidazione se riuscite a darmela.

Ho realizzato un contagiri con un pic per un cliente che aveva bisogno di conoscere approssimativamente (con un errore < 30 giri/min) il numero di giri del suo motore. E' un motore a scoppio monocilindrico di un piccolo mezzo agricolo. Voleva un metodo poco invasivo per poterli visualizzare e ho preso spunto da un contagiri ad induzione che il mio babbo utilizza in campagna su un trattore.

Così ho pensato di sfruttare un pic per leggere gli impulsi (con una oppurtuna elettronica di acqusizione del segnale) provenienti da un filo avvolto a spirale sul cavo candela.

Ho utilizzato un quarzo da 40kHz ed un prescaler di 128 per avere con buona precisione dal TMR0 una base dei tempi di circa 1s e tramite interrupt ho contanto gli impulsi ricevuti in un secondo e moltiplicato per 60 per avere i giri/min.

Tutto funziona bene se non per l'errore di 60 giri/min (soprattutto a basse velocità) che ottengo durante la visualizzazione dei giri su LCD.

Infatti l'errore è formato da passi di 60 g/min, infatti 60 impulsi al secondo = 3600 al minuto mentre 59 implusi al secondo = 3540 il peso di un'impluso in piu o in meno è sempre di 60 g/min.

Avevo pensato anche di aumentare la base dei tempi, diminuendo così la costante moltiplicativa. Solo che in tal modo ho un ritardo nella visualizzazione dei giri.

E' possibile secondo voi una visualizzazione quasi istantanea (max 1s) ma con un errore piccolo inferiore ai 10 giri/min?

Saluti

Edge

Link al commento
Condividi su altri siti


Sarebbe possibile dopo alcuni secondi di funzionamento. pasta fare una base tempi, anche da 0.1", e non resettare il conteggio ad ogni misura ma somamrlo e dividerlo per il numero di letture sino ad un certo numero di letture, 100 p.e.; da quel momento in poi il sistema diviene un FIFO: scarti lettura più vecchia ed inserisci l'ultima.

Link al commento
Condividi su altri siti

Grazie per la risposta Livio.

Allora, vediamo se ho inquadrato la soluzione che mi hai fornito.

Attualmente il programma è realizzato concettualmente in questo modo:

- Inizializzo TMR0 e variabile conteggio "cont";

- incremento variabile "cont" per conteggio impulsi ad ogni interrupt -> cont++;

- Quando TMR0 arriva a 0 eseguo la moltiplicazione del numero di conteggi in un secondo per 60 per ottenere i giri al minuto;

- Riazzero la variabile "cont";

- Re-inizializzo il TMR0.

Nella tua soluzione invece, posso accorciare la base dei tempi, prendiamo per esempio 0,1" come dici. A questo punto procedo come segue:

- Inizializzo TMR0 e variabile conteggio "cont";

- Incremento variabile "cont";

- Ad ogni TMR0 = 0 sommo i conteggi e incremento una variabile associata a TMR0 che mi indica quante letture ho effettuato;

- Dopo un certo numero di letture divido il conteggio per il numero di letture;

- Riazzero "cont";

- Riazzero variabile letture effettuate;

- Re-inizializzo TMR0;

Non sò se ho inquadrato bene il problema. Però mi sfugge come faccio ad avere una misura in giri/min. In questo modo non faccio solo una media?

Saluti

Edge

Link al commento
Condividi su altri siti

Molto frequentemente, per misurare frequenze così basse, viene utilizzato un sistema tipo "periodimetro". In pratica, utilizzando una frequenza di riferimento piu' elevata all'interno del micro (diciamo piu' o meno 1000hz) conti quanti di questi impulsi intercorrono tra un tuo trigger e l'altro (quello che viene dal sensore posto sulla candela).

In questo modo la risoluzione che hai è molto elevata rispetto al numero di giri del motore.

Normalmente è buona pratica procurarsi entrambi i dati, quello del periodimetro e quello del frequenzimetro. A lato di visualizzazione decidi tu quale mostrare in base al numero di giri del motore. Per basse frequenze otterrai una precisione maggiore con il periodimetro.

Un ulteriore filtraggio e mediazione come suggerito da Livio migliora il tutto ...

RealTime

Link al commento
Condividi su altri siti

Ciao,

In pratica i metodi sono:

1) contare tempo per unità di impulsi

2) contare impulsi per unità di tempo

Si decide l'uno /l'altro, o lo switch a seconda delle caratteristiche del sistema e dell micro.

Link al commento
Condividi su altri siti

Grazie RESNIC per la risposta.

Ok, però non riesco a comprendere bene come arrivo ad ottenere in tal modo i giri/min. Cioè io in tal caso sommo il tempo ad ogni ricezione di impulso? Se è cosi, supponiamo di sommare il tempo fino ad 1 secondo. Alla fine non devo sempre moltiplicare per 60 per avere i giri/min. Non avrei lo stesso errore? Mi sfugge il ragionamento.

Edge

Link al commento
Condividi su altri siti

Come ti accennavo prima, il tuo software potrebbe essere "adattivo". A seconda del numero di giri del motore puoi prendere le informazioni dal "periodimetro" (Impulsi su unita' di tempo) o dal frequenzimetro

RealTime

Link al commento
Condividi su altri siti

Si ok.

Va bene. Il discorso non fa una piega. Però la frequenza è riferita al secondo. Per sapere i giri/min devo moltiplicare per 60 come faccio adesso no? Non ho sempre il medesimo errore allora?

Saluti

Edge

Link al commento
Condividi su altri siti

Utilizzando il metodo "periodimetrico" tu garantisci una maggiore risoluzione, e quindi precisione, sulla misura. La risoluzione scelta per il periodimetro deve compensare anche il fattore moltiplicativo che tu vai ad introdurre ( * 60 )

Ovviamente devi considerare la precisione di calcolo nella formula 1/T. Puoi usare floating point se il micro te lo permette oppure puoi eseguire calcoli a virgola fissa (coefficienti moltiplicativi)

RealTime

Link al commento
Condividi su altri siti

Eccomi qua.

Ok. Ho capito. Provo a realizzare il firmware in questo modo. Semmai se ho qualche dubbio e non disturbo ti faccio qualche domanda.

Grazie mille.

Edge

Link al commento
Condividi su altri siti

Eccomi qua. Allora, sto seguendo una strada e vorrei un'opinione se sto proseguendo nel modo corretto.

Ho voluto sfruttare il Timer1 del pic per l'applicazione che è a 16bit (lo uso pochissimo tale registro e quindi sicuramente qualche castroneria la faccio, ma prima o poi devo impararlo bene).

Pensavo di sfruttare un clock maggiore, per esempio 8Mhz con un prescaler di 8. In tal modo ho un periodo di circa 4us. In questo modo ho un periodo massimo misurabile con Timer1 di 2^16 * 4us = 262ms.

Quindi posso misurare una velocità minima di 60/0,262= 228 rpm (che mi va benissimo). Se vado in overflow segno 0 (ma a questo ci pensiamo dopo).

A questo punto inizializzo i vari registri nel main:

INTCON = 0x90; //abilito anche l'interrupt su RB0 da dove mi arriva il segnale

OPTION_REG = 0xC0;

T1CON = 0x31 // Clock interno Fosc/4 e prescaler a 8

TMR1H=0;

TMR1L=0;

Ad ogni interrupt nel ciclo while del main procedo nel modo seguente:

1) disabilito gli interrupt;

2) calcolo gli rpm facendo 60000/periodo_misurato

3) riabilito gli interrupt

4) visualizzo risultato su LCD

E nella routine di interrupt? Nella ISR procedo nel modo seguente:

1) Leggo il valore a 16bit di TMR1 e quello è il periodo e lo memorizzo nella variabile perido = TMR1H<<8+TMR1L;

2) Riazzero TMR1 conto nuovamente da 0;

3) esco dall'interrupt;

Ho provato il tutto e il risultato è che sull'LCD visualizzo costantemente 65535!

Perchè? wallbash.gif

Link al commento
Condividi su altri siti

Ovviamente bisogna vedere il codice :-|

comunque,

Non esiste la necessità di disabilitare gli interrupts.

Il tuo software necessita di due base tempi. Uno e' il timer ad alta risoluzione

che ti permette di effettuare il calcolo dei ticks trascorsi (4uS), l'altro è il trigger

che arriva dal motore.

Quello che devi fare e' misurare quanti ticks sono passati tra due triggers consecutivi.

La cosa e' abbastanza semplice

Hai prima di tutto bisogno di un semaforo di sincronizzazione. Questo perche' i processi che agiscono sulla medesima variabile sono 2 (il main e l'interrupt)

FACCIO UNA PREMESSA: Le primitive di sincronizzazione (Semafori) sono argomenti non cosi' banali. non dare nulla per scontato.

Nel dettaglio tu devi trovare il modo per garantire l'accesso ad un singolo processo alla volta. Il discorso e' veramente lungo. Nella letteratura questi oggetti si chiamano piu' propriamente MUTEX.

La tua routine di interrupt del trigger (Segnale candela) agisce come segue

1) Controlli il valore del semaforo, se questo e' ROSSO allora salti al passo (3). In questo caso, dato che siamo in una condizione di "concorrenza" decidiamo di non effettuare il prelievo dell'informazione.

2) Congela il valore del timer#1 TMR1x. Lo metti su qualche variabile globale per esempio

3) Azzera nuovamente il TMR1x

4) Esce

Nella main:

1) Imposti il semaforo come ROSSO. In questo modo il valore proveniente dall'interrupt non verra' alterato.

2) Fai una copia del valore letto in modo da congelare questo risultato

3) Imposti il semaforo come VERDE. Ora l'interrupt puo' nuovamente alterare il valore dei ticks trascorsi

4) usi il valore copiato per i tuoi calcoli. Il valore che hai è il tempo trascorso tra due triggers consecutivi

5) Attendi un certo periodo, questo e' il periodo tra due campionamenti consecutivi

6) GOTO (1)

Come vedi anche su una cosa cosi' piccola ci sarebbe da discutere per ore

RealTime

Link al commento
Condividi su altri siti

Bhè, il codice per ora è molto semplice e scarno, comunque te lo posto lo stesso:

// Inizializzazione LCD su cui indicare i giri/min

sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RC7_bit;
sbit LCD_D7 at RC6_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISC7_bit;
sbit LCD_D7_Direction at TRISC6_bit;

int periodo=0;
int ricevuto=0;
int risultato=0;
int cont=0;
char testo[6];


void interrupt()
{
ricevuto=1;
periodo=TMR1H<<8+TMR1L;
TMR1H=0;
TMR1L=0;
INTCON.INTF=0;


}


void main()

{
PORTB = 0; // Initialize PORTB
PORTC = 0;
TRISB = 1; // PORTB is output


OPTION_REG=0xC0;
INTCON=0x90;

Lcd_Init(); // Initialize LCD

Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
Delay_ms(500);
T1CON=0x31;
TMR1H=0;
TMR1L=0;




/*----------------------------------------------------------------------------*/


while(1)
{

if(ricevuto==1)

{
INTCON.GIE=0;
INTCON.INTE=0;
risultato=60000/periodo;
ricevuto=0;
INTCON.GIE=1;
INTCON.INTE=1;
}

WordToStr(risultato,testo);
Lcd_Out(2,1,testo);
}


}

E' come te l'ho descritto nel messaggio di prima.

Link al commento
Condividi su altri siti

Devi fare un po' di prove

Prima di tutto con l'oscilloscopio controlla se effettivamente i tuoi triggers arrivano correttamente alla routine di interrupt. Puoi alzare ed abbassare un bit quando entri nell'handler suddetto. Poi colleghi tale pin all'oscilloscopio e verifichi che tutto sia ok

Puoi anche visualizzare il valore della variabile "periodo" e controllare se i valori assunti sono sempre all'interno di un certo range da te stimato.

Il ciclo while che tu hai scritto e' un po intenso, nel senso che non lascia TREGUA al processore smile.gif

Chiami in continuazione la coppia di funzioni

WordToStr(risultato,testo);

Lcd_Out(2,1,testo);

Devi farlo solo se il valore cambia, oppure usi altri metodi per ottimizzare il tutto

RealTime

Link al commento
Condividi su altri siti

Si certo. Sono tutte prove che faccio sempre per togliermi tutti i dubbi derivanti da anomalie diciamo dell'elettronica.

Comunque da quel punto di vista non ci sono problemi per ora.

Ecco, questo può essere un'idea. Per vedere se il valore cambia però dovrei memorizzare il valore precedente e quello successivo giusto?

Link al commento
Condividi su altri siti

Mi sembra che Livio ti abbia già dato una risposta esaustiva all'inizio del thread, ovvero, se campioni più letture e le sommi per poi le dividerle per il numero degli stessi da te scelto, otterrai una media sempre più precisa che con il tuo sistema...

Oltre a non dare aria al processore, il display a quelle velocità non ha neppure il tempo di mostrarti correttamente la lettura, almeno questo è ciò che penso io...

Link al commento
Condividi su altri siti

Tutto dipende dalla risposta che vuoi dal sistema.

Con il metodo delle medie il s/w impiega piu' tempo per andare a regime e per ottenere un valore accurato. Se hai necessita' di una misura puntuale e dinamica deve seguire strade come sopra descritte

Ovviamente nessuna perdita di tempo e pazienza smile.gif

RealTime

Link al commento
Condividi su altri siti

Se usi una coda circolare il tempo è lungo solo per la prima media, poi si rinfresca ad ogni base tempi di conteggio.

Però con quel numero di impulsi a giro la soluzione più idonea è il periodimetro. Se proprio si è in fena di sofisticherie si può realizzare sia il periodimetro che il frequenzimetro ed avere due valori: uno su breve periodo e l'altro su lungo periodo.

Ora ripensando al tipo di captatore torno ai miei 20 anni (quindi indietro di quasi mezzo secolo sad.gif ) quando mi occupavo proprio di strumentazione per elettrauti. All'ora i micro non esistevano e per molti tecnici anche i transistors eran cosine incomprensibili perchè non si illuminavano. biggrin.gif

L'impulso della candela, per un 4 cilindri, capita ogni 4 giri. Un motore d'automobile ha un minimo attorno agli 800 rpm, quindi la frequenza minima captata è circa 3,33 Hz (800/60/4), corrispondenti a 300 ms.

Basta collegare il capatore all'interrupt esterno del micro. Poi al primo fronte si abilita uno dei timer del PIC; al successivo impulso si blocca, si legge il valore e lo si resetta facendolo poi ripartire.

Con la frequenza di quarzo dichiarata la precisione dovrebbe eesere elevata anche a 6500 rpm (27 Hz <==>36,923 ms).

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

Sì, diciamo che l'effetto definitivo è una forma di Smoothing-Filter con un suo tempo di convoluzione. La profondità della coda decide il tempo di risposta

RealTime

Link al commento
Condividi su altri siti

Grazie mille Livio per la risposta. Sicuramente sbaglio qua allora. E' che continuo ad utilizzare Timer1 in maniera diversa da Timer0, mentre alla fine mi basta operare allo stesso modo.

Quindi arriva il primo fronte come interrupt su RB0, attivo di conseguenza Timer1, al successivo fronte lo blocco e quello che leggo è il mio periodo.

A questo punto se la mia risoluzione è R (esempio 4usec) e T è il periodo misurato tra i due fronti di salita, la formula diventa:

rpm = 60 / (T * R) = (60/R) / T

dove T deve essere espresso in secondi (4e-6 in tal caso).

Il ragionamento dovrebbe essere giusto.

Link al commento
Condividi su altri siti

Non esattamente. La profondità della coda decide il tempo di ritardo del filtro mentre, a regime, il tempo di campionamento equivale al tempo di rinfresco del valore calcolato.

E' la classica media scorrevole o "slide window" se preferisci la terminologia yankee smile.gif

NO, non dovrebbe ma è giusto. smile.gif

A 6500 rpm avresti ancora 9259 conteggi di clock, valore che ti permette di discriminare anche frazioni di rpm.

Piuttosto dovrai provvedere ad una stabilizzazione delle letture con eventuali medie.

Modificato: da Livio Orsini
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...