Port Manipulation Encoder

Ciao a tutti. Devo controllare un rotary encoder per interfaccia utente e devo modificare il codice in "true C" per velocizzare le letture. Sto usando Arduino Uno (Atmega328).

Il codice di partenza é questo:

encoderLast=LOW;
n=LOW;
void setup(){
pinMode(2,INPUT);
pinMode(3,INPUT);
digitalWrite(2,HIGH);
digitalWrite(3,HIGH);
}
void loop(){
encoder();
}
void encoder(){
  n = digitalRead(2);
   if ((encoderLast == LOW) && (n == HIGH)) {
    if (digitalRead(3) == LOW) {
       encoderPos--;
     } else {
       encoderPos++;
     }
   } 
   encoderLast = n;
}

Ora dovrei velocizzare la funzione encoder utilizzando la port manipulation:

void encoder(){
  n = PIND & _BV(PIND2);
   if ((encoderLast == LOW) && (n == HIGH)) {
    if ((PIND & _BV(PIND3)) == LOW) {
       encoderPos--;//antiorario
     } else {
       encoderPos++;//orario
     }
   } 
   encoderLast = n;
}

Il risultato della modifica é che la variabile encoderPos non si incrementa ne si decrementa ed é come se nelle condizioni vi sia qualcosa che non va. Potete aiutarmi? Grazie

Secondo me ti conviene usare un interrupt, così non perdi nessun impulso.

PaoloP:
Secondo me ti conviene usare un interrupt, così non perdi nessun impulso.

Quoto

Purtroppo non posso usare gli interrupt perché mi da problemi con il display.
Inoltre vorrei imparare meglio la port manipulation. Secondo voi il codice é corretto?

n = PIND & _BV(PIND2);

Se n viene dichiarato come intero 0000100 vale 4, e nel blocco if non viene riconosciuto come HIGH

sciorty:

n = PIND & _BV(PIND2);

Se n viene dichiarato come intero 0000100 vale 4, e nel blocco if non viene riconosciuto come HIGH

Perché HIGH è una costante predefinita che vale 1.
Come LOW è una costante che vale 0.

Grazie, ora funziona!
Ho applicato la stessa funzione ad un encoder Hall di un motore che fa al massimo 80 giri/minuto (rapporto riduzione 131:1), con una risoluzione di 64 cpr, ma di cui utilizzo un solo canale, perché la direzione la prendo da una variabile impostata altrove. (non posso usare gli interrupt)
Dopo alcuni calcoli, ho fatto in modo che ad ogni 25 impulsi letti (solo sul fronte di salita) mi faccio aumentare una variabile per indicarmi i millimetri percorsi. Ora per distanze che vanno da 5mm fino ad 80 mm la precisione é buona. Oltre gli 80 mm inizio ad avere imprecisioni non uniformi.
Tengo a precisare che ho modificato gran parte delle funzioni presenti nel codice con quelle proprie della port manipulation ed ora tutto il funzionamento mi sembra molto piú fluido e senza ritardi.

Cosa posso fare per aumentare la precisione?

Io ho appena costruito un oggetto vagamente simile al tuo, con display grafico abbastanza impegativo, e per variate alcuni parametri ho usato un encoder rotativo (a mò di Volume autoradio), usando gli interrupt sui pin 2 e 3.

Mi spieghi che problemi avresti con il display, visto che non usa alcun timer interno (unico motivo per cui un Interrupt ext non dovrebbe funzionare)?

Solo usando gli interrupt puoi avere una "fluidità" di utilizzo soddisfacente.

Tieni sempre a mente che l'ambizione e coronamento finale di un "Power user" di MCU, è quella di non scrivere NULLA nella funzione while(), nel caso di Arduino la funzione loop().

BaBBuino:
Tieni sempre a mente che l'ambizione e coronamento finale di un "Power user" di MCU, è quella di non scrivere NULLA nella funzione while(), nel caso di Arduino la funzione loop().

Direi che hai appena scritto una cosa che viola tutte le regole della buona programmazione.
E' esattamente l'opposto di quello che dici, nel main loop, tipicamente una while(1), viene fatto tutto, ovviamente chiamando funzioni esterne scritte appositamente, mentre nelle isr, che devono durare il minimo indispensabile, ci si deve limitare a resettare gli eventuali flag, svolgere solo le operazioni che richiedono attenzione immediata e settare un eventuale flag per informare il main loop che deve eseguire una certa azione.

Guarda... mi stai mandando in depressione perchè ho giusto litigato la settimana scorsa con un Ingegnere che sosteneva quanto ho detto. Siccome sembrava sapesse il fatto suo, ho abbozzato e fatta mio questa osservazione.

Lui insisteva su una programmazione simil Windows, ovvero programmazione attivata da eventi. Funzioni che partono solo se si è innescato l'evento, un interrup. Naturalmente con una ben studiata gestione delle priorità degli int.

Io invece sostenevo che usare solo interrupt porta a un gran casino, e che una gestione sensata del loop fosse piú funzionale.

Se mi dici bene come sostenere la tesi contro il "tutto fuori", torno a litigarci di corsa!

BaBBuino:
Lui insisteva su una programmazione simil Windows, ovvero programmazione attivata da eventi

Non è possibile fare paragoni tra un programma scritto per Windows tramite un compilatore ad oggetti, con relativo supporto, e un programma scritto per una mcu 8 bit, sono due cose totalmente diverse come approccio, funzionamento e stile di scrittura.
Il software event driven per Windows si basa sopratutto sugli interrupt software, che sono decisamente diversi da quelli hardware, anche perché il kernel di windows non consente di accedere direttamente all'hardware, poi ci sono i timer che permettono di far partire ogni tot ms (con largo margine di errore) un certo processo, inoltre anche rimanendo sul C++ per Windows non esiste proprio la main(), almeno in apparenza, in realtà c'è anche se non la vedi, più o meno come fa la loop() di Arduino.

Io invece sostenevo che usare solo interrupt porta a un gran casino, e che una gestione sensata del loop fosse piú funzionale.

L'approccio corretto, parlando di C, è avere un main loop, non importa se si chiama main() o loop(), dentro il quale gestisci il tutto in base ad una serie di flag e/o temporizzazioni.
Un buon modo di gestire il tutto è usare un timer come clock di sistema che ogni tot us, normalmente va bene una volta ogni ms (come fa la millis ) attiva la sua isr al cui interno vengono incrementate delle variabili che sono l'orologio di vari eventi, se ne usa più di una perché spesso fa comodo azzerarle ad ogni ciclo in modo da poter usare variabili al massimo di tipo int e non avere mai problemi di overflow.
Nella main/loop hai una cascata di if, o delle switch, che controllano i flag delle azioni, questi vengono settati all'interno delle isr o come risultanza di operazioni eseguite, in base ai quali vengono chiamate specifiche funzioni, idem con i timer che attivano ad intervalli regolari determinati processi.
Poi ci sarebbero da fare delle considerazioni sulle precedenze di alcuni processi su altri, sopratutto se è richiesto un preciso intervallo temporale di esecuzione con un jitter minimo, però qui stiamo entrando nel campo degli RTOS che è un discorso molto complesso, se serve realmente è meglio usarne uno già fatto e sicuramente funzionante.

BaBBuino:
ho giusto litigato la settimana scorsa con un Ingegnere che sosteneva quanto ho detto.

Dimenticavo, ingegnere in cosa e che esperienza ha sulla programmazione di micro/mcu ?

Purtroppo il personaggio è sopra le righe, è stato allievo di un certo Dott. Zener (!) alla Westinghouse e si occupa di robotica industriale (sistemi di azionamento motori brushless bracci robot). Uno di quelli che conosce a memoria ogni bit di Canbus, Modbus, Profibus ecc.
Non è uno di quei ingegneri da 1000 lire al kilo, quindi quando parla ha il suo fascino e ci litigo solo perchè è un caro amico.

Ma se ho modo ci litigo volentieri! :smiley:

BaBBuino:
Ma se ho modo ci litigo volentieri! :smiley:

Dato che nemmeno io sono da 1000 lire al kg se vuoi ci litigo io per te :grin:
Però non mi hai ancora detto in cosa è ingegnere, il fatto che si occupa di azionamenti non significa che li progetta lui, anzi è facile che utilizza sistemi che ho progettato io :smiley: :grin: :smiley:

Oh perbacco qui la discussione si fa a .... "chi ce l'ha più lungo" :smiley: :grin: :smiley: :grin:

BaBBuino:
Io ho appena costruito un oggetto vagamente simile al tuo, con display grafico abbastanza impegativo, e per variate alcuni parametri ho usato un encoder rotativo (a mò di Volume autoradio), usando gli interrupt sui pin 2 e 3.

Mi spieghi che problemi avresti con il display, visto che non usa alcun timer interno (unico motivo per cui un Interrupt ext non dovrebbe funzionare)?

Solo usando gli interrupt puoi avere una "fluidità" di utilizzo soddisfacente.

Tieni sempre a mente che l'ambizione e coronamento finale di un "Power user" di MCU, è quella di non scrivere NULLA nella funzione while(), nel caso di Arduino la funzione loop().

Utilizzo display glcd con controller ST7565 (ST7565 LCD tutorial) e relative librerie che non mi consentono di usare interrupt, pena lo sputtanamento del display e relativo reset.

Se aumento il numero degli impulsi acquisisti, prelevandoli non solo dal fronte di salita ma anche da quello di discesa, avrei miglioramenti oppure la situazione sarebbe la stessa?

astrobeed:

BaBBuino:
Ma se ho modo ci litigo volentieri! :smiley:

Dato che nemmeno io sono da 1000 lire al kg se vuoi ci litigo io per te :grin:
Però non mi hai ancora detto in cosa è ingegnere, il fatto che si occupa di azionamenti non significa che li progetta lui, anzi è facile che utilizza sistemi che ho progettato io :smiley: :grin: :smiley:

Nono, progetta tutto lui dall'analogica di potenza alla gestione di Pole displacement e Space vector, con tanto di brevetto su una specie di resolver. Ora che mi dici che sei del settore non ti dico che ditta è... :smiley:

Cmq un cranio sopra le righe...

Adesso dò un'occhiata al Display (ma perchè non un più comune KS0108?). Cmq alla fine mi sono dovuto rassegnare anchio e utilizzare gli ingressi Interrupt per l'encorder, perchè leggendo con digitalRead() veniva una schifezza, specie ruotando veloce la manopola. Invece con l'Int la sensazione che quello che muovi conla mano e quello che vedi sul Display, è perfetta.

Ho vistio il Display...

Ammazza che carriola! :smiley: E' na schifezza!

Cmq, con grande probabilità è per il fatto che utilizza la comunicazione SPI il tuo problema di Iterrupt.

Io ho un KS0108 (ma va bene anche un T6963). Ho la limitazione della connessione parallela ( servono tutti gli 8 bit, da DB0 a DB7), ma non ho problemi di Interrupt. E con il tuo oggetto sarebbe bene avere funzionali.

BaBBuino:

astrobeed:

BaBBuino:
Ma se ho modo ci litigo volentieri! :smiley:

Dato che nemmeno io sono da 1000 lire al kg se vuoi ci litigo io per te :grin:
Però non mi hai ancora detto in cosa è ingegnere, il fatto che si occupa di azionamenti non significa che li progetta lui, anzi è facile che utilizza sistemi che ho progettato io :smiley: :grin: :smiley:

Nono, progetta tutto lui dall'analogica di potenza alla gestione di Pole displacement e Space vector, con tanto di brevetto su una specie di resolver. Ora che mi dici che sei del settore non ti dico che ditta è... :smiley:

Cmq un cranio sopra le righe...

Aspettavo la replica di Astro... :stuck_out_tongue: