PCF8574 un expander i2c I/O digitale: Arduino, esp8266 e esp32, encoder rotativo – Parte 2
Un trasduttore di posizione angolare, chiamato anche encoder rotativo, è un dispositivo elettromeccanico che converte la posizione angolare o il movimento di un albero o di un asse in segnali di uscita analogici o digitali.
Introduzione agli encoder
Gli encoder rotativi sono utilizzati in una vasta gamma di applicazioni che richiedono il monitoraggio o il controllo, o entrambi, di sistemi meccanici, inclusi controlli industriali, robotica, obiettivi fotografici, dispositivi di input del computer come mouse e trackball optomeccanici e piattaforme radar rotanti .
Puoi acquistare l’encoder o il modulo già pronto per la prototipazione.
Puoi trovare il rotary encoder su AliExpress
Puoi trovare il modulo rotary encoder module su AliExpressIl modulo è più semplice da gestire, il problema più fastidioso di un encoder rotativo è il disturbo che lo caratterizza. Il modulo ha solo un semplice resistore pull-up per ridurre il rumore, ma se vuoi usare l’encoder in formato nativo è meglio se oltre ad applicare un resistore pull-up applicare anche un semplice filtro.
Per pull-up utilizzo una resistenza da 10k collegata al VCC, per il filtro utilizzo una resistenza da 10k dall’encoder e un condensatore da 100nF collegato al GND.
L’encoder funziona con la rotazione della piastra collegata agli interruttori.
Il movimento genera una sequenza predeterminata:
Quindi dobbiamo decodificare questa sequenza per ottenere rotazione e direzione.
Libreria
Puoi trovare la mia libreria qui o direttamente dal Library manager dell’Arduino IDE.
Download
Fare clic sul pulsante DOWNLOADS nell’angolo in alto a destra, rinominare la cartella non compressa PCF8574.
Verificare che la cartella PCF8574 contenga PCF8574.cpp e PCF8574.h.
Collocare la cartella della libreria PCF8574 come / librerie / cartella.
Potrebbe essere necessario creare la sottocartella librerie se è la tua prima libreria.
Riavvia l’IDE.
IC or Module
Puoi usare un normale IC o un modulo.
Puoi trovare il pcf8574 su AliExpress
Puoi trovare il modulo pcf8574 module su AliExpressSchema di connessione
Come spiegato per collegare un encoder al pcf8574 o anche al microcontrollore, è necessario un filtro per ridurre il rumore o almeno utilizzare un modulo.
Qui uno schema di connessione completo sulla breadboard.
Il risultato in questa foto.
Sketchs di esempio
Utilizzo base
Ecco uno schizzo di base che utilizza una funzionalità di base della libreria.
/*
* PCF8574 GPIO Port Expand
* https://mischianti.org
*
* PCF8574 ----- WeMos
* A0 ----- GRD
* A1 ----- GRD
* A2 ----- GRD
* VSS ----- GRD
* VDD ----- 5V/3.3V
* SDA ----- D1(PullUp)
* SCL ----- D2(PullUp)
* INT ----- INT(PullUp)
*
* P0 ----------------- ENCODER PIN A
* P1 ----------------- ENCODER PIN B
* P2 ----------------- ENCODER BUTTON
*
*/
#include "Arduino.h"
#include "PCF8574.h"
int encoderPinA = P0;
int encoderPinB = P1;
#define INTERRUPTED_PIN D7
void ICACHE_RAM_ATTR updateEncoder();
// initialize library
PCF8574 pcf8574(0x38, INTERRUPTED_PIN, updateEncoder);
volatile long encoderValue = 0;
uint8_t encoderButtonVal = HIGH;
void setup()
{
Serial.begin (9600);
delay(500);
// encoder pins
pcf8574.pinMode(encoderPinA, INPUT_PULLUP);
pcf8574.pinMode(encoderPinB, INPUT_PULLUP);
// encoder button
pcf8574.pinMode(P2, INPUT_PULLUP);
// Set low latency with this method or uncomment LOW_LATENCY define in the library
// Needed for encoder
// pcf8574.setLatency(0);
// Start library
pcf8574.begin();
}
bool changed = false;
void loop()
{
if (changed){
Serial.print("ENCODER --> ");
Serial.print(encoderValue);
Serial.print(" - BUTTON --> ");
Serial.println(encoderButtonVal?"HIGH":"LOW");
changed = false;
}
}
uint8_t encoderPinALast = LOW;
uint8_t valPrecEncoderButton = LOW;
void updateEncoder(){
// Encoder management
uint8_t n = pcf8574.digitalRead(encoderPinA, true);
if ((encoderPinALast == LOW) && (n == HIGH)) {
if (pcf8574.digitalRead(encoderPinB, true) == LOW) {
encoderValue--;
changed = true; // Chnged the value
} else {
encoderValue++;
changed = true; // Chnged the value
}
}
encoderPinALast = n;
// Button management
encoderButtonVal = pcf8574.digitalRead(P2);
if (encoderButtonVal!=valPrecEncoderButton){
changed = true; // Chnged the value of button
valPrecEncoderButton = encoderButtonVal;
}
}
Ci sono alcune cose importanti da notare:
Nell’esp8266 per creare un callback per l’interrupt è necessario specificare ICACHE_RAM_ATTR
.
void ICACHE_RAM_ATTR updateEncoder();
I pin dell’encoder sono INPUT_PULLUP
// encoder pins
pcf8574.pinMode(encoderPinA, INPUT_PULLUP);
pcf8574.pinMode(encoderPinB, INPUT_PULLUP);
Per utilizzare l’encoder con il pcf8574 devi azzerare la latenza e ci sono 3 modalità possibili:
- usa il metodo
pcf8574.setLatency(0);
per impostare la latenza a 0; - rimuovi il commento alla define
// #define PCF8574_LOW_LATENCY
; - o meglio solo per l’encoder usa
pcf8574.digitalRead(encoderPinA, true)
con il parametro addizionare che serve ad azzerare la latenza a true;
Avremo molte letture false e duplicate (l’interrupt viene generato da ogni variazione), anche se abbiamo impostato il filtro.
Quindi nella procedura verificheremo quando il valore è stato modificato tramite da una variabile changed
.
Utilizzo ottimizzato delle funzionalità della libreria
La libreria dispone di una serie di metodi per eseguire tutte le operazioni in maniera trasparente.
Invece di usare pinMode
puoi usare direttamente il metodo encoder
così:
PCF8574::encoder(uint8_t pinA, uint8_t pinB)
e la dichiarazione nel nostro casò diventerà:
// encoder pins
pcf8574.encoder(encoderPinA, encoderPinB);
per gestire la logica dell’encoder sono a disposizione 2 metodi il primo restituisce la variazione del tipo -1, 0 or 1.
int8_t PCF8574::readEncoderValue(uint8_t pinA, uint8_t pinB)
e la logica precedentemente descrittà diventerà:
int vale = pcf8574.readEncoderValue(encoderPinA, encoderPinB);
if (vale!=0){
changed = true;
}
encoderValue = encoderValue + vale;
un altro semplice metodo è:
bool PCF8574::readEncoderValue(uint8_t pinA, uint8_t pinB, volatile long *encoderValue)
che ritorna se l’encoder è variato (-1 o +1) e passando il riferimento della variabile encoderValue il metodo aggiorna il valore internamente.
Così il metodo diventerà:
changed = pcf8574.readEncoderValue(encoderPinA, encoderPinB, &encoderValue);
Qui come diventa lo sketch precedente:
/*
* PCF8574 GPIO Port Expand
* https://mischianti.org
*
* PCF8574 ----- WeMos
* A0 ----- GRD
* A1 ----- GRD
* A2 ----- GRD
* VSS ----- GRD
* VDD ----- 5V/3.3V
* SDA ----- D1(PullUp)
* SCL ----- D2(PullUp)
* INT ----- INT(PullUp)
*
* P0 ----------------- ENCODER PIN A
* P1 ----------------- ENCODER PIN B
* P2 ----------------- ENCODER BUTTON
*
*/
#include "Arduino.h"
#include "PCF8574.h"
int encoderPinA = P0;
int encoderPinB = P1;
#define INTERRUPTED_PIN D7
void ICACHE_RAM_ATTR updateEncoder();
// initialize library
PCF8574 pcf8574(0x38, INTERRUPTED_PIN, updateEncoder);
volatile long encoderValue = 0;
uint8_t encoderButtonVal = HIGH;
void setup()
{
Serial.begin (9600);
delay(500);
// encoder pins
pcf8574.encoder(encoderPinA, encoderPinB);
// encoder button
pcf8574.pinMode(P2, INPUT_PULLUP);
// Start library
pcf8574.begin();
}
bool changed = false;
// The loop function is called in an endless loop
void loop()
{
if (changed){
Serial.print("ENCODER --> ");
Serial.print(encoderValue);
Serial.print(" - BUTTON --> ");
Serial.println(encoderButtonVal?"HIGH":"LOW");
changed = false;
}
}
bool valPrec = LOW;
void updateEncoder(){
changed = pcf8574.readEncoderValue(encoderPinA, encoderPinB, &encoderValue);
// int vale = pcf8574.readEncoderValue(encoderPinA, encoderPinB);
// if (vale!=0){
// changed = true;
// }
// encoderValue = encoderValue + vale;
bool val = pcf8574.digitalRead(P2);
if (val!=valPrec){
changed = true;
valPrec = val;
}
}
Video dimostrativo
Grazie
- PCF8574 un expander i2c I/O digitale: Arduino, esp8266 e esp32, I/O ed interrupt
- PCF8574 un expander i2c I/O digitale: Arduino, esp8266 e esp32, encoder rotativo