Lettura dell'encoder rotativo Arduino migliorata

Gli encoder rotativi sono ottimi dispositivi di input per progetti di elettronica - si spera che questo Instructable possa ispirare e aiutarti a utilizzarne uno nel tuo prossimo progetto.

Perché scrivere il codice del codificatore rotante?

Volevo usare un codificatore rotante a basso costo come meccanismo di input per uno dei miei prossimi progetti e inizialmente ero sconcertato dalle opzioni di codice disponibili per prendere le letture dal codificatore rotante e determinare quanti "fermi" o cicli in cui l'encoder aveva fatto clic e in quale direzione. Penso che il mio schizzo principale dovrà usare la maggior parte della memoria del mio Arduino, quindi sto evitando le varie librerie di encoder disponibili, che sembravano difficili da far funzionare quando ne ho provate un paio. Sembrano anche utilizzare molto più del budget del codice rispetto agli approcci basati su sketch discussi di seguito.

Se vuoi solo bypassare il pensiero dietro il mio approccio e entrare direttamente nell'Instructable, sentiti libero di saltare al Passaggio 1!

Altri approcci

Molti dei principali approcci basati su sketch (cioè non usano una libreria) sono discussi nel post del blog di rt in cui scrivono un codice encoder rotativo che rende gli encoder più economici utilizzabili come input Arduino. Hanno anche un buon esempio del loro segnale logico prodotto dall'encoder. Ho scoperto che un sistema di interruzione del timer ha funzionato meglio per loro, ma sono preoccupato che la frequenza di polling ridurrebbe la velocità di aggiornamento dello schermo nel ciclo principale del mio schizzo del progetto. Dato che il codificatore rotante si sposterà per una piccola parte del tempo in cui voglio che lo schermo si aggiorni, questa sembra una corrispondenza scadente per la mia applicazione.

Ho scelto di iniziare a utilizzare qui il codice di Steve Spence, che andava bene da solo, ma sembrava rallentare davvero quando ho incorporato il resto del mio codice di schizzo (che comporta la scrittura di aggiornamenti di visualizzazione su un piccolo schermo TFT). Inizialmente mi chiedevo se potesse essere perché il ciclo principale contiene un'istruzione debounce.

Ho quindi letto l'articolo del codificatore rotante di Oleg su una versione di routine del servizio di interruzione del suo precedente post, ho anche pensato che sarebbe una buona idea usare la manipolazione diretta delle porte per leggere entrambi i pin contemporaneamente e non appena l'interruzione scatta. Il suo codice può essere utilizzato su qualsiasi pin di input se il codice di manipolazione della porta viene riscritto. Al contrario, ho deciso di utilizzare solo gli interrupt di processo sui pin digitali 2 e 3, in modo da poter impostare gli interrupt in modo che si attivino solo su un fronte di salita della tensione del pin, piuttosto che sulla variazione di tensione del pin, che include i fronti di discesa. Ciò riduce il numero di volte in cui viene chiamato l'ISR, distogliendo dal circuito principale.

Il codice di Oleg utilizza una tabella di ricerca per ridurre le dimensioni del codice compilato a dimensioni molto ridotte, ma non sono riuscito a ottenere risultati affidabili che catturerebbero una rotazione molto lenta e una rotazione ragionevolmente veloce. Tieni presente che il debouncing dell'hardware (vedi Passaggio 2) può aiutare molto con l'affidabilità delle letture, ma cercavo una soluzione software per semplificare la creazione dell'hardware ed essere il più portatile possibile per altre applicazioni hardware.

Questo conclude l'introduzione della mia sfida e delle mie considerazioni. Nel passaggio 2 daremo un'occhiata all'hardware dell'encoder, alla terminologia e ad alcune considerazioni pratiche quando si desidera integrare un encoder rotativo nel proprio progetto.

Passaggio 1: informazioni sugli encoder rotativi

Perché gli encoder rotativi sono così belli?

  1. A differenza di un resistore / potenziometro variabile, hanno una corsa infinita in qualsiasi direzione e poiché producono un "codice Gray" digitale, è possibile ridimensionare le loro letture a qualsiasi intervallo desiderato.
  2. La doppia direzione li rende utili per aumentare o diminuire un valore all'interno di una variabile o navigare nei menu.
  3. Infine, molti di questi codificatori rotativi sono dotati di un pulsante centrale, che può essere utilizzato per selezionare voci di menu, ripristinare un contatore o fare qualsiasi cosa si possa pensare che possa adattarsi a un pulsante momentaneo.

condizioni

  1. PPR: impulsi per rotazione - in genere 12, 20 o 24. Potresti anche vedere le specifiche per la massima rotazione in giri / min. Ecc. Ciò è probabilmente determinato dalla propensione dell'encoder a "rimbalzare" i contatti - vedi sotto.
  2. Detentivo: il piccolo clic dell'azione che si genera in un punto di riposo naturale tra gli impulsi. Può esserci un arresto per impulso / ciclo (non uguale a una rotazione dell'albero) o due.
  3. Rimbalzo: i contatti meccanici all'interno dell'encoder rimbalzano letteralmente abbastanza da saltare indietro e indietro su un contatto durante la rotazione, portando potenzialmente a troppe letture attribuite a quella fase del viaggio tra i fermi.
  4. Debounce: questo può essere fatto sia in hardware, magari con un condensatore ceramico di basso valore tra ciascun pin e terra, o in software, forse con un ritardo. In entrambi i casi, l'obiettivo è quello di creare un sistema che ignori i contatti che rimbalzano.

Suggerimenti

  1. Cerca una sezione filettata vicino alla base dell'albero e un dado corrispondente se desideri montare l'encoder in un pannello o in una custodia.
  2. Per gli encoder rotativi sono disponibili molte manopole, le più facilmente disponibili sono disponibili con alberi di diametro 6 mm.
  3. Prestare attenzione a se l'albero dell'encoder utilizza una faccia piatta o scanalature per ottenere un adattamento corretto con la manopola.
  4. Il corpo dell'encoder rotativo può anche venire con un perno / troncone rialzato, destinato ad accoppiarsi con un piccolo rientro / foro nel pannello (probabilmente nascosto dalla manopola) e impedire la rotazione dell'encoder quando si gira la manopola. Potresti scoprire di volerlo rimuovere se riesci a creare un attrito sufficiente a impedire la rotazione del corpo dell'encoder usando il bullone di montaggio per avvitare l'encoder nel pannello o nella custodia.
  5. Assicurati di scoprire dove si trova lo stato di arresto per il tuo codificatore e adatta il tuo codice di conseguenza. Il mio esempio usa un encoder i cui pin sono entrambi scollegati da terra e sono tirati in alto dai rispettivi resistori di pullup in ingresso. Questo guida la mia selezione di un interruzione RISING. Se entrambi i pin fossero collegati a terra quando erano in arresto, avrebbero bisogno del codice che cercava la tensione dei pin FALLING.

Step 2: The Circuit

Il circuito è così semplice. Avrai bisogno:



• Un Arduino basato su ATMEGA328P, come Uno, Pro Mini o Nano.
• Un encoder rotativo meccanico in quadratura (anziché ottico): questo è il tipo più comune, quindi non preoccuparti troppo se non specificato. Gli elenchi eBay e Aliexpress citeranno spesso Arduino nella descrizione e questo è un buon indicatore che uno è adatto.
• Cavo di collegamento / cavi di collegamento.
• Opzionale: una breadboard di prototipazione.


Prima di tutto, cerca una raccolta di tre pin su un lato dell'encoder. Questi sono i tre per misurare la rotazione con il nostro codice. Se ci sono due perni insieme su un altro lato, è probabile che si tratti del pulsante centrale. Li ignoreremo per ora.

Dei tre pin insieme, il pin di terra dell'encoder è collegato al pin di terra di Arduino. Ognuno degli altri due pin è collegato al pin digitale 2 e il restante acceso è collegato al pin digitale 3. Se il senso di rotazione non è come desiderato, scambiare semplicemente i due pin non di terra.

I pin 2 e 3 sono importanti perché sugli Arduinos basati su ATMEGA328P sono gli unici pin che hanno la capacità di rilevare interruzioni di cambio pin RISING e FALLING. Le schede MEGA 2560 ecc. Hanno altri pin di interruzione hardware che possono farlo.

Nota: nel diagramma il perno di terra è uno dei perni di estremità. In realtà, il pin di messa a terra è spesso il pin centrale, ma non è sempre così, quindi leggi la scheda tecnica o testa l'encoder per scoprire quale pin è collegato a terra.

Un'altra nota: ArneTR ha commentato bene di non avere una connessione cablata separatamente alla tensione logica positiva (ad es. 5 V o 3, 3 V) per il circuito dell'encoder rotativo mostrato. L'Arduino non può leggere l'encoder rotativo senza un segnale di terra (a cui abbiamo collegato un filo) e la tensione logica (talvolta annotata come Vcc o Vdd), quindi come può l'Arduino leggere la logica da questo encoder senza un positivo filo di tensione? La risposta è che il chip ATMEGA328P nell'Arduino ha una modalità speciale che puoi impostare sui pin digitali (che stiamo usando) in cui un pin viene automaticamente portato "alto" alla tensione logica da un resistore interno. Cerca nel codice "pinMode (pinX, INPUT_PULLUP)" per vederci dire ad Arduino che vogliamo sfruttare questa modalità. Una volta impostato, dobbiamo solo fornire all'encoder un filo di terra poiché i fili di rilevamento dai pin digitali forniscono già la tensione logica.

UN ALTRO COSA ... Githyuk ha scoperto che un particolare codificatore di marca non funzionava con questo modo di fare le cose (cioè il codice sotto). Si prega di consultare la sezione commenti per i dettagli, ma in generale, provare un codificatore diverso sarebbe un buon passo di debug quando hai esaurito i passaggi più facili / veloci / più economici.

Passaggio 3: il codice

Se non si ha familiarità con la programmazione di Arduinos, informarsi su questa risorsa dagli stessi Arduino.

Questo codice è gratuito per il tuo utilizzo (come gratuito e da modificare a tuo piacimento), ti preghiamo di attribuire dove dovresti.


/ ******* Schizzo dell'encoder rotativo basato su interruzioni *******
di Simon Merrett, basato sull'intuizione di Oleg Mazurov, Nick Gammon, RT, Steve Spence
* /

pin int statico A = 2; // Il nostro primo pin di interruzione hardware è il pin 2 digitale
pin int statico B = 3; // Il nostro secondo pin di interruzione hardware è il pin 3 digitale
byte volatile aFlag = 0; // facci sapere quando ci aspettiamo che un fronte di salita sul pinA segnali che l'encoder è arrivato a un arresto
byte volatile bFlag = 0; // facci sapere quando ci aspettiamo che un fronte di salita sul pinB segnali che l'encoder è arrivato a un arresto (direzione opposta a quando è impostato aFlag)
encoder byte volatile Pos = 0; // questa variabile memorizza il nostro valore corrente della posizione dell'encoder. Passare a int o uin16_t invece di byte se si desidera registrare un intervallo maggiore di 0-255
byte volatile oldEncPos = 0; // memorizza l'ultimo valore della posizione dell'encoder in modo da poterlo confrontare con la lettura corrente e vedere se è cambiato (quindi sappiamo quando stampare sul monitor seriale)
lettura di byte volatile = 0; // da qualche parte per memorizzare i valori diretti che leggiamo dai nostri pin di interruzione prima di verificare se abbiamo spostato un intero arresto

void setup () {
pinMode (pinA, INPUT_PULLUP); // imposta il pinA come input, portato ALTO alla tensione logica (5 V o 3, 3 V per la maggior parte dei casi)
pinMode (pinB, INPUT_PULLUP); // imposta pinB come input, portato ALTO alla tensione logica (5 V o 3, 3 V per la maggior parte dei casi)
attachInterrupt (0, PinA, crescenti); // imposta un interrupt su PinA, cercando un segnale di fronte ascendente ed eseguendo la routine di servizio di interrupt "PinA" (sotto)
attachInterrupt (1, PINB, crescenti); // imposta un interrupt su PinB, cercando un segnale di fronte ascendente ed eseguendo la routine di servizio di interrupt "PinB" (sotto)
Serial.begin (115200); // avvia il collegamento al monitor seriale
}

void PinA () {
cli (); // impedisce che si verifichino interruzioni prima di leggere i valori dei pin
lettura = PIND & 0xC; // legge tutti gli otto valori dei pin quindi rimuove tutti i valori tranne pinA e pinB
if (reading == B00001100 && aFlag) {// verifica che entrambi i pin siano in arresto (HIGH) e che ci aspettiamo un arresto sul fronte di salita di questo pin
encoderPos -; // decrementa il conteggio della posizione dell'encoder
bFlag = 0; // ripristina i flag per il turno successivo
aFlag = 0; // ripristina i flag per il turno successivo
}
altrimenti if (lettura == B00000100) bFlag = 1; // segnala che ci aspettiamo che pinB segnali la transizione verso l'arresto dalla rotazione libera
sei (); // riavvia interruzioni
}

void PinB () {
cli (); // impedisce che si verifichino interruzioni prima di leggere i valori dei pin
lettura = PIND & 0xC; // legge tutti gli otto valori dei pin quindi rimuove tutti i valori tranne pinA e pinB
if (reading == B00001100 && bFlag) {// controlla che entrambi i pin siano in arresto (HIGH) e che ci aspettiamo un arresto sul fronte di salita di questo pin
encoderPos ++; // incrementa il conteggio della posizione dell'encoder
bFlag = 0; // ripristina i flag per il turno successivo
aFlag = 0; // ripristina i flag per il turno successivo
}
altrimenti if (lettura == B00001000) aFlag = 1; // segnala che ci aspettiamo che il pinA segnali la transizione verso l'arresto dalla rotazione libera
sei (); // riavvia interruzioni
}

void loop () {
if (oldEncPos! = encoderPos) {
Serial.println (encoderPos);
oldEncPos = encoderPos;
}
}

Questo è tutto!

allegati

  • rotaryEncoder.ino Scarica

Passaggio 4: conclusione

Spero che questo codice sia utile per il tuo prossimo progetto che utilizza un codificatore rotante o che ti ha ispirato a considerare un codificatore rotante come input per il tuo prossimo progetto.

Sommario degli obiettivi

Ho provato a scrivere un codice che raggiunga un buon equilibrio di:

  • Portabilità (il codice di manipolazione delle porte è il compromesso quando si passa ad altri chip)
  • Velocità (la manipolazione delle porte aiuta davvero)
  • Dimensione del codice compilata bassa (manipolazione delle porte e bitmath aiutano)
  • Registra in modo affidabile la rotazione manuale lenta e veloce
  • Chiamate di routine ridotte del servizio interrupt nugatorio (utilizzando l'interruzione RISING e disabilitando temporaneamente gli interrupt)

Avvertenze e idee per il miglioramento

Questo codice non è perfetto in alcun modo e potresti volerlo cambiare per usare altri pin. Ho testato questo codice con lo schizzo che stava causando il maggior numero di ritardi e letture meno affidabili con gli altri approcci discussi - di certo non l'ho confrontato con i timer per vedere il cui codice produce meno routine di servizio di interruzione obbligatoria, impiega meno tempo per eseguire o filtra la percentuale più alta di rimbalzi di contatto. Forse qualcuno potrebbe voler fare un test di riferimento contro gli altri approcci là fuori.

Articoli Correlati