Acquisizione rotazione encoder (con sorgenti) - rotary encoder acquisition (with sources)

Gestione della rotazione di un encoder con verifica dello stato di fermo e della direzione di rotazione. Utilizza gli interrupt per agganciare la ricezione degli impulsi di rotazione.


Sull'encoder ho calettato un motorino per fare dei test sulla velocita di acquisizione.
Per ogni unità di tempo l'Arduino invia il numero di impulsi, positivi o negativi, ricevuti dall'ultima lettura.
 L'RTC serve per loggare l'impulso con il time.


// Comment
// ***********************************
// Test Lettura Encoder con logger video
// Reteservizi di Paolo Fiaschi
// 2016
// ***********************************

#include 
#include 
#include "RTClib.h"

RTC_DS1307 rtc;
elapsedMillis timeElapsed; 

DateTime now;

const int BUFFSIZE = 20; // dimensioni buffer
char buffer[BUFFSIZE];   // buffer per la ricezione dalla seriale

String Buff = "";
boolean codeValid = false;
boolean debug = false;      

unsigned long previousMillis = 0;        

unsigned int interval = 2000;           // timeout della ricezione dati seriale
unsigned long currentMillis;
unsigned long Contatore=0;
 
int encoderPin1 = 2;
int encoderPin2 = 3;
int ledLeftPin  = 5;
int ledRightPin = 6;
int ledStopPin  = 7;
int ledAlarmPin  = 8;

int Direzione=0;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;
volatile long localEncoderValue = 0;

long lastEncoderValue = 0;
long checkEncoderValue= 0;

int lastMSB = 0;
int lastLSB = 0;

// ******************************************** 
void setup() {
  Serial.begin (115200);
  
  // *** init RTC
  Wire.begin();
  rtc.begin();

  // **************
  // settaggio RTC
  // **************
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
  // ***
 
  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);
  pinMode(ledLeftPin, OUTPUT);
  pinMode(ledRightPin,OUTPUT);
  pinMode(ledStopPin, OUTPUT);
  pinMode(ledAlarmPin, OUTPUT);
  
  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  // interrupt agganciati sul cambio di stato delle porte
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);
}

// ********************************************** 
void loop(){
  // la lettura dell'encoder viene fatta dagli interrupt.

  if (timeElapsed > interval) {
    // errore timeout
    //if(debug) Serial.println("Errore Timeout Comunicazione");
    digitalWrite(ledAlarmPin,HIGH);
  }
 
  if (Serial.available() > 0) 
  {
    timeElapsed = 0;
    digitalWrite(ledAlarmPin,LOW);
    
    int index=0;
    //delay(10); // aspetta si riempia il buffer (non serve per 1 solo carattere)
    
    int numChar = Serial.available();
    
    if (numChar > BUFFSIZE) numChar = BUFFSIZE;
    
    while (numChar--) {
      buffer[index++] = Serial.read();
    }
    splitString(buffer); // Elabora la stringa ricevuta
  }

  // gestione indicatori di direzione
  if(lastEncoderValue==localEncoderValue) Direzione=0;

  if(Direzione==0) {
    digitalWrite(ledLeftPin,LOW);
    digitalWrite(ledRightPin,LOW);
    digitalWrite(ledStopPin,HIGH);
  }
  
  if(Direzione==1) {
    digitalWrite(ledLeftPin,HIGH);
    digitalWrite(ledRightPin,LOW);
    digitalWrite(ledStopPin,LOW);
  }

  if(Direzione==2) {
    digitalWrite(ledLeftPin,LOW);
    digitalWrite(ledRightPin,HIGH);
    digitalWrite(ledStopPin,LOW);
  }

  lastEncoderValue=localEncoderValue;
  
  delay(5); // serve solo per dare tempo ai led di accendersi.
}

// ***************************************** 
void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //MSB = bit + significativo
  int LSB = digitalRead(encoderPin2); //LSB = bit - significativo
 
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
 
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) {
    encoderValue ++; Direzione=1;
    
    if(encoderValue>200000) { // per evitare l'overflow
      encoderValue=1;
      lastEncoderValue=0; 
    }
  }
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) {
    encoderValue --; Direzione=2;
  
    if(encoderValue<-200000 and="" ata="" break="" buff.concat="" buffer="" buffers="" c="Check" caratteri="" case="" char="" check="" clear="" codevalid="true;" command="" commands="" converte="" data="ConvData(now);" de-tokenize="" debug="" do="" e="Encoder" each="" encodervalue="" entered:="" erial.println="" evitare="" for="" from="" i="" if="" in="" int="" l="" lastencoded="encoded;" lastencodervalue="0;" list="" localencodervalue="encoderValue;" next="" now="" null="" ontatore="" onvdata="" overflow="" parameter="strtok" parse="" parsecmd="" pass="" per="" processed="" received="" remove="" result="" ricevuti="" sequentially="" serial.flush="" serial.print="" serial.println="" serial="" splitstring="" store="" stringa="" switch="" text="" the="" this="" time="" to="" tring="" value="" void="" while="" x="">2000000000) Contatore=0; // resetta il long
      
      Serial.print(++Contatore);
      Serial.print("-");
      
      Serial.println(encoderValue);
      encoderValue=0; // una volta inviato il valore lo azzera per ripartire
      codeValid = true;
      break;

    case ('T'): //check T=Timer
      switch(data[1])
      {
        case('T'):  // Time (TT)
          now = rtc.now();  
          Serial.println(ConvData(now));      
          codeValid = true;
          break;

        case('S'):  // Time setting xxddmmyyyyhhmmss (TS)
          String str(data);
          if(str.length()==18) {  // ci sono anche i CRLF
            String anno=str.substring(6,10);
            String mese=str.substring(4,6);
            String giorno=str.substring(2,4);
            String ora=str.substring(10,12);
            String minuti=str.substring(12,14);
            String secondi=str.substring(14,16);
            
            rtc.adjust(DateTime(anno.toInt(), mese.toInt(), giorno.toInt(), ora.toInt(), minuti.toInt(), secondi.toInt()));
            
            now = rtc.now();  
            //Serial.print("Nuova Data:"); 
            Serial.println(ConvData(now));      
            codeValid = true;
          }
          break;
      }      
   
    default:
      if(debug)
      {
        Serial.println(data); 
      }
    }
  
  //if(codeValid) Serial.println(buffer); //echo back what is received
  codeValid = false;
  Buff = ""; //Reset passing string
}  

// *********************************
String ConvData(DateTime Data) {

/*    
String result="";

char buffer[30];
PString str(buffer, sizeof(buffer));
str = Data.day(); // assignment
str += "/";
str += Data.month(); // concatenation
str += "/";
str += Data.year(); // concatenation
str += " ";
str += Data.hour(); // concatenation
str += ":";
str += Data.minute(); // concatenation
str += ":";
str += Data.second(); // concatenation
*/

char buf[30];
sprintf(buf, "%02d/%02d/%04d %02d:%02d:%02d",Data.day(), Data.month(), Data.year(), Data.hour(), Data.minute(), Data.second()); 
 
return buf;
}

2 commenti:

  1. Buongiorno Paolo: Mi permetto di disturbarti per chiederti un consiglio sulla lettura degli encoder. Premetto che sono a zero con la conoscenza di Arduino. Ti spiego la mia situazione. Devo misurare i gradi in Azimut e Elevazione di una antenna, usando 2 encoder incrementali. Ho studiato un pò gli sketch che ho trovato in giro e usando Arduino Mega che ha 3 ingressi Interrupt sono riuscito a leggere e scrivere su un I2C LCD la lettura di uno dei due encoder: quello che è identificato in queste righe "void updateEncoder(){
    int MSB = digitalRead(encoderPinA); //MSB = most significant bit
    int LSB = digitalRead(encoderPinB); //LSB = least significant bit. E qui mi sono bloccato perchè non so come dire al microproc. di leggere e scrivere i dati anche dell altro encoder (encoderPinC) e (encoderPinD). In pratica non so come e dove mettere l'istruzione per far si che mi venga letto anche l'altro valore e naturalmente venga inviato al display. Ho bisogno insomma di chiamare i 2 dispositivi Encoder 1 e Encoder 2 o A e B. Se vuoi e ti interessa la sfida ti posso inviare il codice da me buttato giu. Se non hai voglia e tempo nessun problema. Grazie comunque per avermi letto. Carlo Carini

    RispondiElimina
    Risposte
    1. Scusa ma ti leggo solo ora, spero che tu abbia già risolto il tuo problema. Comunque, se ho capito la tua domanda, se tu leggi già gli interrupt per l'encoder 'A' ti basta 'duplicare' quello che hai già fatto, agganciando i pin dell' encoder B ai pin di interrupt rimasti liberi, che sul mega mi pare siano 6 disponibili e tutto dovrebbe funzionare allo stesso modo.

      Per esempio potresti fare:

      int encoder2Pin1 = 18;
      int encoder2Pin2 = 19;

      digitalWrite(encoder2Pin1, HIGH); //turn pullup resistor on
      digitalWrite(encoder2Pin2, HIGH); //turn pullup resistor on

      attachInterrupt(digitalPinToInterrupt(encoder2Pin1), updateEncoder2, CHANGE);
      attachInterrupt(digitalPinToInterrupt(encoder2Pin2), updateEncoder2, CHANGE);

      e duplichi la updateEncoder in updateEncoder2 o meglio ancora riconosci nella updateEncoder quale dei due encoder ha generato la chiamata.

      Buon lavoro.

      Paolo

      Elimina

Ciao, lascia un tuo commento o suggerimento relativo a quello che hai trovato su questo blog.