Buonasera a tutta la community di arduino,e complimenti per il forum!
Sono dyk e da poco che studio informatica ed elettronica,sono alle prime armi,ed e anche da poco che mi sono appassionato ad arduino.
Questo e il mio primo post,ho visto che siete molto gentili e aiutate tutti,quindi vengo al dunque:
Mi chiedevo se qualcumo mi potesse aiutare a visualizzare il valore dell'encoder sul display lcd i2c.
Il problema che mi da e quello di sfasarsi quasi da subito non conta bene,salta all'improvviso di migliaia e addirittura a valori negativi.
l'encoder che stai usando ha le uscite open collector, e quindi per funzionare bene ha bisogno di delle resistenze di pull-up di circa 1K.
Le hai messe?
Non so a che velocita' fai girare l'encoder ma considerando che ha 2500 impulsi al giro ti consiglio di velocizzare al massimo l'interrupt.
Per fare questo crea una funzione di interrupt per ogni segnale ed usa la lettura diretta della porta.
Attenzione perche' usando la lettura diretta della porta il codice diventa legato ad una specifica Arduino.
IL mio esempio e’ per la UNO.
Inoltre quando leggi il valore del contatore nel main devi disablilitare gli interrupt perche' potresti leggere un valore sbagliato se cade un interrupt proprio in quel momento.
Questo perche' in un micro ad 8 bit la lettura di una long (4 bytes) non puo essere effettuata in una sola istruzione (si dice che non e' una operazione atomica) e quindi se cade un interrupt li in mezzo tu leggi alcuni bytes della long con i valori vecchi ed altri con i valori nuovi.
Nel tuo caso questo porterebbe solo degli errori di visualizzazione ma se poi usi il valore letto dall'encoder avresti dei comportamenti sbagliati.
Ultima cosa: per visualizzare il valore sul display o sulla seriale di debug usa millis() per farlo 4 o 5 volte al secondo.
Qui trovi un esempio di codice:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f, 16, 2);
int encoderPin1 = 2;
int encoderPin2 = 3;
volatile long encoderValue = 0;
unsigned long Millis;
unsigned long LcdTime;
//------- setup -------------------------------------------------------------------------
void setup()
{
lcd.begin();
lcd.setCursor(0,0);
lcd.print("millimetri");
Serial.begin(115200);
pinMode(encoderPin1,INPUT);
pinMode(encoderPin2,INPUT);
attachInterrupt(0,Pin1_Isr,CHANGE); // ogni filo dell'encoder ha una sua funzione
attachInterrupt(1,Pin2_Isr,CHANGE); // di interrupt per velocizzarla
LcdTime = millis();
}
//------ loop ---------------------------------------------------------------------------
void loop()
{
unsigned long encoderValue_temp;
Millis = millis(); // usa millis() per aggiornare il display
if (Millis - LcdTime > 250) // 4 volte al secondo
{
LcdTime = Millis;
noInterrupts(); // disabilita gli interrupt per farti una copia del
encoderValue_temp = encoderValue; // contatore dell'encoder che verra' usata dal main
interrupts(); // e poi riabilita gli interrupt
Serial.println(encoderValue,DEC);
lcd.setCursor(4,1);
lcd.print(encoderValue);
}
}
//------ interrupt functions ------------------------------------------------------------
// nelle funzioni di interrupt usa
// l'accesso diretto alla porta per
// velocizzare al massimo l'esecuzione
// !!! attenzione facendo cosi' il codice
// diventa dipendente dall'Arduino che usi
// questo funziona sulla UNO !!!
void Pin1_Isr()
{
if ( ((PIND & B00001100) == B00000000) || ((PIND & B00001100) == B00001100) )
encoderValue++;
else
encoderValue--;
}
void Pin2_Isr()
{
if ( ((PIND & B00001100) == B00000000) || ((PIND & B00001100) == B00001100) )
encoderValue--;
else
encoderValue++;
}
mi sono accorto che ho caricato la variabile EncoderValue_temp e poi non la ho usata
questo e' il codice corretto:
noInterrupts(); // disabilita gli interrupt per farti una copia del
encoderValue_temp = encoderValue; // contatore dell'encoder che verra' usata dal main
interrupts(); // e poi riabilita gli interrupt
Serial.println(encoderValue_temp,DEC);
lcd.setCursor(4,1);
lcd.print(encoderValue_temp);
grazie marco gentilissimo.
si uso arduino uno,ho messo le due resistenze di pull up come mi hai suggerito tu.
Ieri ho provato e non andava,ho corretto il codice ma adesso appena tocco l'encoder mi appare un numero di dieci cifre,e non decresce ne misura valori negativi.
La mia idea era quella di misurare una lunghezza con molta precisione,muovendo a mano,
per poi tramutare il valore dell'encoder in millimetri.
no sulla seriale e piu regolare,ma credo che conti piu di quanto dovrebbe in un giro dell'encoder conta da 0 a 7-8 mila quando dovrebbe essere 2500,invece non mostra valori negativi,cioe se si va sotto allo zero va direttamente a 42miliardi e dispari positivi.
Invece sul display dovunque lo giro va a 42miliardi sempre positivi.
encoderValue_temp deve essere dichiarato come long e non unsigned long
Questo giustifica perche' non compaiono valori negativi ma non i conteggi errati.
Trovo strano che sul display e sulla seriale ci siano comportamenti diversi.
Non posso verificare il display perche' non ne ho uno, ma a me sulla seriale funziona bene anche se con un encoder diverso dal tuo e commentando le linee che riguardano il display.
Sei certo del funzionamento del tuo encoder?
Hai modo di controllare i segnali con un oscilloscopio?
E' una domanda banale ma come hai collegato l'encoder e le resistenze di pull up?
Marco grazie ancora che mi stai aiutando
allora con la modifica e molto piu regolare conta perfettamente nella seriale,anche con valori negativi ma sul display conta bene sia in positivo sia in negativo, ma se si cerca di tornare indietro si sfasa e fa salti di migliaia o decine di migliaia.
Non ho modo di controllare l'encoder, e le resistenze le ho messe cosi in parallelo che partono dal pin 2 e 3 e vanno nel 5 volt.
Pultroppo non so mettere una foto dello schema ne l'indirizzo
Non capisco come sia possibile che la seriale ed il display si comportino in modo diverso visto che gli si passa la stessa variabile encoderValue_temp ...
Quando dici conta bene intendi anche che in un giro fa l'esatto numero di conteggi che dovrebbero essere 2500 ?
Una prova significativa e' segnare la posizione dell'albero dell'encoder ed il numero di impulsi in quella posizione, poi ruotare l'albero a caso in senso orario ed antiorario e poi riportarlo alla posizione iniziale: il numero di impulsi letto deve essere uguale al valore di partenza.
marco mi devi scusare,mi sono accorto adesso dell'errore,che cretino che sono...
il problema e che sul display conserva delle cifre!!
cioe mi spiego quando sali in positivo e arrivi a 10mila poi quando riscendi a non so 800 conserva le due cifre(anche se cambiano andando avanti o indietro), lo stesso in negativo.
ecco perche lo vedovo passare in decine in decine XD.
per il resto conta benissimo pero in un giro completo fa 10mila invece che 2500 e ritornando indietro ritorna approssimativamente allo 0 diciamo,ripetendo piu volte il valore resta stabile.
E’ sempre una fonte di equivoco il numero di tacche presente sul disco dell’encoder ed il numero di impulsi per giro ed il rapporto e’ 1 a 4 per cui direi che e’ giusto che tu abbia 10000 conte al giro.
Mi dici che libreria stai usando per il display e che modello e’ il display.
Penso che prima di fare la print del valore sul display dovresti fare lcd.print(“ “) con un adeguato numero di spazi in modo da cancellare il valore precedente.
Forse il display flickerera’ un po ma dovresti avere una visualizzazione corretta.
ragazzi scusate se ravvivo un po il post.
Nello sketch postatomi gentilmente da sulimarco(funzionante perfettamente),riscontro un problema.
Il problema strano sta nella visualizzazione del valore dell'encoder sul display(16x2-i2c).
Premetto di aver provato 3 display diversi e 3 librerie differenti di visualizzazione.
Il problema sta che se le cifre aumentano per esempio da 1,2,3.. a 10567 è poi giro l'encoder nel verso opposto il display conserva le cifre.
per esempio se da 10567 diminuisco a 105 il display o il codice conserva le cifre 67,è questo avviene sempre e piu caselle occupa il valore poi nel diminuire piu numeri conserva.
Non so se mi sono spiegato bene.
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd(0x27);
int encoderPin1 = 2;
int encoderPin2 = 3;
volatile long encoderValue = 0;
unsigned long Millis;
unsigned long LcdTime;
//------- setup -------------------------------------------------------------------------
void setup()
{
lcd.begin(16, 2);
//lcd.init();
lcd.setBacklight(255);
lcd.setCursor(0,0);
lcd.print("millimetri");
Serial.begin(115200);
pinMode(encoderPin1,INPUT);
pinMode(encoderPin2,INPUT);
attachInterrupt(0,Pin1_Isr,CHANGE); // ogni filo dell'encoder ha una sua funzione
attachInterrupt(1,Pin2_Isr,CHANGE); // di interrupt per velocizzarla
LcdTime = millis();
}
//------ loop ---------------------------------------------------------------------------
void loop()
{
long encoderValue_temp;
Millis = millis(); // usa millis() per aggiornare il display
if (Millis - LcdTime > 250) // 4 volte al secondo
{
LcdTime = Millis;
noInterrupts(); // disabilita gli interrupt per farti una copia del
encoderValue_temp = encoderValue; // contatore dell'encoder che verra' usata dal main
interrupts(); // e poi riabilita gli interrupt
Serial.println(encoderValue_temp,DEC);
lcd.setCursor(0,1);
lcd.print(encoderValue_temp);
}
}
//------ interrupt functions ------------------------------------------------------------
// nelle funzioni di interrupt usa
// l'accesso diretto alla porta per
// velocizzare al massimo l'esecuzione
// !!! attenzione facendo cosi' il codice
// diventa dipendente dall'Arduino che usi
// questo funziona sulla UNO !!!
void Pin1_Isr()
{
if ( ((PIND & B00001100) == B00000000) || ((PIND & B00001100) == B00001100) )
encoderValue++;
else
encoderValue--;
}
void Pin2_Isr()
{
if ( ((PIND & B00001100) == B00000000) || ((PIND & B00001100) == B00001100) )
encoderValue--;
else
encoderValue++;
}
Si, ti sei spiegato bene, ma pero' non leggi quello che ti scrivono
Penso che prima di fare la print del valore sul display dovresti fare lcd.print(" ") con un adeguato numero di spazi in modo da cancellare il valore precedente.