Mmmhh...
A dire il vero a me non piace come lo hai impostato, per alcune ragioni:
- non mi è chiaro il modo che hai scelto per acquisire gli impulsi encoder;
- non capisco perché vuoi tirare in ballo 3 timer differenti;
- non mi è chiaro il voler moltiplicare il numero degli impulsi per 600 (o meglio, questo aspetto lo chiariamo poi, un'idea ce l'ho);
- ci sono un po' di errore "formali";
- non hai indicato la velocità massima del motore e quella stimata, secondo i tuoi calcoli, sulla ruota alla quale applichi l'encoder.
Io ti suggerisco questo approccio, poi sei libero di fare come credi, ovviamente

I segnali in arrivo dall'esterno devono essere acquisiti. Il PIC16F877A ha due modalità per acquisire segnali veloci dall'esterno: INT e IOC. Siccome dobbiamo acquisire due segnali con un solo PIC, dobbiamo purtroppo scartare l'ipotesi INT e concentrarci su IOC.
IOC lavora sulle porte RB4, ..., RB7. Scegliamo RB4 e RB5
Partiamo con configurarlo:
Variabili globali e macro che serviranno nel codice:
- Codice: Seleziona tutto
// Macro
#define AVANTI 1
#define INDIETRO 0
// Variabili globali
volatile unsigned int uiEncoderDx, uiEncoderSx; // Impulsi encoder per ciascuna ruota
volatile char chActualStatubPortb; // Variabile di appoggio per la lettura della porta PORTB
volatile unsigned int uiSpeedDx, uiSpeedSx; // Velocità delle due ruote
long lGlobalCounterDx, lGlobalCounterSx; // Contatori globali dello spazio percorso
char flag_Movimento;
- Codice: Seleziona tutto
void InitPic (void)
{
TRISB = 0x30; // Corrisponde a 0011 0000, RB4 e RB5 INPUT, gli altri OUTPUT
INTCON.RBIE = 1; // Abilita solamente Interrupt On Change
uiEncoderDx = 0;
uiEncoderSx = 0;
lGlobalCounterDx = 0;
lGlobalCounterSx = 0;
// Da inserire: tutte le altre impostazioni e l'abilitazione dell'interrupt generale
// .........
}
All'interno della Interrupt Service Routine (ISR) andiamo a gestire l'evento IOC; come dice l'espressione, Interrupt On Change indica lo scatenarsi di una interruzione a fronte di una variazione di stato logico della porta RB4, ..., RB7. La variazione di stato l'abbiamo tanto per il passaggio da 0 a 1 logico quanto viceversa. Per convenzione (scelta da me, in questo caso), stabiliamo che un segnale è considerato "da acquisire" quando passa da 0 a 1 mentre va scartato nel caso opposto.
- Codice: Seleziona tutto
// ISR
void interrupt (void)
{
// Gestione IOC
if (INTCON.RBIF) // Si è scatenato un interrupt on change. Ma quale???
{
chActualStatubPortb = PORTB; // Leggo la porta PORTB per conoscere i valori di RB4 e RB5
if (chActualStatubPortb&0x10) // Se è RB4 è a 1
{
uiEncoderDx++; // incrementa il contatore impulsi encoder ruota DX
if (flag_Movimento == AVANTI) // Incrementa o decrementa la distanza globale percorsa dalla ruota DX
{
lGlobalCounterDx++;
} else {
lGlobalCounterDx--;
}
}
if (chActualStatubPortb&0x20) // Se è RB4 è a 1
{
uiEncoderSx++; // incrementa il contatore impulsi encoder ruota SX
if (flag_Movimento == AVANTI) // Incrementa o decrementa la distanza globale percorsa dalla ruota SX
{
lGlobalCounterSx++;
} else {
lGlobalCounterSx--;
}
}
}
// TIMER 0
//...
}
A questo punto valutiamo la velocità cui ruota ogni singolo encoderr, come numero di impulsi riferiti all'unità di tempo. Immaginiamo che l'unità di tempo sia il il centesimo di secondo (10ms). All'interno dell'interrupt andiamo ad inserire la gesione del calcolo della velocità.
- Codice: Seleziona tutto
void interrupt (void)
{
// Gestione IOC
//... non la ripeto, tanto è già scritta prima
// TIMER 0
if (INTCON.T0IF)
{
uiSpeedDx = uiEncoderDx;
uiSpeedSx = uiEncoderSx;
uiEncoderDx = 0;
uiEncoderSx = 0;
INTCON.T0IF = 0;
}
}
La scelta degli interi (int) è una opzione da valutare attentamente sulla base della risoluzione che avrà il tuo disco encoder e della velocità di rotazione massima alla quale si muove. Se si stima che ogni 10ms il numero degli impulsi può superare i 65535, è necessario passare ai long. Attenzione, però: l'uso dei long potrebbe far "esplodere" il codice in quanto vuoi far gestire a una macchiana 8 bit, dati a 32 bit.
Ho inserito anche il conteggio della strada percorsa da ciascuna ruota. Il flag fl_Movimento devi imporlo tu, nel codice, al valore AVANTI o INDIETRO per stabilire se incrementare o decrementare le distanze percorse.
Ti è chiaro come ho impostato il problema? IOC per il conteggio degli impulsi e TMR0 per il calcolo della velocità.
Prova a pensarci su e dimmi se è un approccio che riesci a seguire.
Ciao.
Paolo.