KY-040 (posiblemente cualquier encoder) y RP2040 (o cualquier otro micro)

hola, buenas tardes,

estuve buscando bastante un código para un ky040 y encontré un monton de los más dispares, algunos que utilizaban librerías en asm y después de cansarme, empecé a probar y llegué a este código que comparto que se basa en muy pocas líneas

#define ENC_ROT_CLK   15
#define ENC_ROT_DT    14

volatile bool KY040estClock = false;

void INT_KY040_SWITCH(void)
{
  KY040estSwitch = true;
}

void setup()
{
  attachInterrupt(digitalPinToInterrupt(ENC_ROT_CLK), INT_KY040_CLOCK, FALLING);
}

void loop()
{
  static long posEncoder=0;

  if ( KY040estClock )
  {
    if ( atenderGiroKY040() ) posEncoder++;
    else posEncoder--;

    KY040estClock = false;
  }
}

bool atenderGiroKY040(void)
{
  if ( digitalRead(ENC_ROT_DT) == 0 ) return true;
  else return false;
}

el módulo ky040 tiene dos pul-up para data y clock pero el switch va directo, así que es necesario ponerle una, yo le puse una r de 10k

después de probar este código y agregarle la atención de la pulsación del switch necesitaba acelerar el conteo en más y en menos para llegar fácil a números altos, así que le agregué un par de funciones más que aparecen en el código que sigue

no se si existe alguna forma de hacerlo mejor, y si existe, por favor, agradecería que la agreguen

este código lo uso en una raspi pico, supongo que anda en cualquier otro micro, por lo menos debería andar con algunos pequeños cambios, por ejemplo el core mbed (ver primer comentario) para el rp2040 no acepta la interrupción por flanco descendente, salta sea descendente o ascendente, lo van a ver en el código, corrigiendo esas pocas cosas debería andar incluso en un pic

este es el código completo para el encoder


#define __DEBUG__

//------------------------------------------------------------------------------
//---- KY-040 ----

/* pines del encoder rotativo */
#define ENC_ROT_SW    13
#define ENC_ROT_CLK   15
#define ENC_ROT_DT    14

/*
 * tiempo en milisegundos que deben pasar entre dos pulsos del encoder para que,
 * menos de ese tiempo, por cada pulso del encoder los saltos sean el resultado
 * de la resta deeste número menos el tiempo transcurrido entre dos pulsos
 * intervalo = KY040_TIEMPO_MIN_ENTRE_PULSOS - tiempo_entre_pulsos
 */
#define KY040_TIEMPO_MIN_ENTRE_PULSOS   50

/*
 * indican con true si salto alguna de las interrupciones
 */
volatile bool KY040estSwitch = false;
volatile bool KY040estClock = false;

/******************************************************************************/
/**** INTERRUPCIONES ****/
/*
 * los módulos KY-040 no llevan pull-up
 * se debe conectar una pull-up (10k)
 */
void INT_KY040_SWITCH(void)
{
  if ( digitalRead(ENC_ROT_SW) == LOW )
  {
    KY040estSwitch = true;
  }
}

//------------------------------------------------------------------------------
/*
 * interrupcion por flanco descendente usada como base
 * para conocer el sentido de giro
 * si data esta en 0, esta girando en el sentido horario
 * y si data esta en 1 esta girando en el sentido anti horario
 */
void INT_KY040_CLOCK(void)
{
  if ( digitalRead(ENC_ROT_CLK) == LOW )
  {
    KY040estClock = true;
  }
}

/******************************************************************************/

void setup()
{
#ifdef __DEBUG__
  Serial.begin (115200);
  while (!Serial) ;
#endif
  
  //----------------------------------------------------------------------------
  //---- KY-040 ----

  /* los módulos KY-040 incluyen una pull-up para clock y para data */
  pinMode (ENC_ROT_CLK, INPUT);
  pinMode (ENC_ROT_DT, INPUT);

  /* 
   * para RP2040
   * en otro caso es posible usar interrupcion por flanco descendente
   * y descartar el chequeo del flanco en la atencion de la interrupcion
   */
  attachInterrupt(digitalPinToInterrupt(ENC_ROT_SW), INT_KY040_SWITCH, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENC_ROT_CLK), INT_KY040_CLOCK, CHANGE);

  //----------------------------------------------------------------------------
}

/******************************************************************************/

void loop()
{
  static long posEncoder=0;
  
  if ( KY040estSwitch )
  {
    delay(20); /* anti rebote */

#ifdef __DEBUG__
    Serial.println("Switch presionado");
#endif

    /* libera la atencion de la interrupcion */
    KY040estSwitch = false;
  }

  if ( KY040estClock )
  {
    uint8_t intervalo = KY040_IntervaloEntrePasos();

    /*
     * true si gira en sentido horario
     * y false si gira en sentido anti horario
     */
    if ( atenderGiroKY040() ) posEncoder += intervalo;
    else posEncoder -= intervalo;

#ifdef __DEBUG__
    Serial.println(posEncoder);
#endif

    /* libera la atencion de la interrupcion */
    KY040estClock = false;
  }
}

/******************************************************************************/
/*
 * retorna true si gira en sentido horario
 * y false si gira en sentido anti horario
 */
bool atenderGiroKY040(void)
{
  /*
   * la interrupcion del clock se activa por flanco descendente
   * entonces
   * si data esta en 0, esta girando en el sentido horario
   */
  if ( digitalRead(ENC_ROT_DT) == 0 ) return true;
  /*
   * pero si data esta en 1
   * esta girando en el sentido anti horario
   */
  else return false;
}

//------------------------------------------------------------------------------
/*
 * mide el tiempo en milisegundos entre dos llamadas
 */
unsigned long KY040_TiempoEntrePulsos(void)
{
  static unsigned long mSegAnt=0, mSegAct=0;
  
  mSegAnt=mSegAct;
  mSegAct=millis();
  return (mSegAct-mSegAnt);
}

//------------------------------------------------------------------------------
/*
  * si el tiempo entre dos pulsos es menor que KY040_TIEMPO_MIN_ENTRE_PULSOS
  * retorna la diferencia entre el tiempo entre los dos pulsos y 
  * KY040_TIEMPO_MIN_ENTRE_PULSOS, y si es mayor, retorna 1 para que el retorno
  * sea sumado o restado del valor actual
  */
uint8_t KY040_IntervaloEntrePasos(void)
{
  unsigned long tiempoEntrePulsos = KY040_TiempoEntrePulsos();

  if ( tiempoEntrePulsos < KY040_TIEMPO_MIN_ENTRE_PULSOS )
  {
    return (KY040_TIEMPO_MIN_ENTRE_PULSOS - tiempoEntrePulsos);
  }
  else
  {
    return 1;
  }
}

saludos,

Según la hoja de datos, sí acepta interrupciones por flanco descendente.


2.19.3. Interrupts
An interrupt can be generated for every GPIO pin in four scenarios:
• Level High: the GPIO pin is a logical 1
• Level Low: the GPIO pin is a logical 0
• Edge High: the GPIO has transitioned from a logical 0 to a logical 1
• Edge Low: the GPIO has transitioned from a logical 1 to a logical 0


Saludos

Agrego:

Te confirmo que

attachInterrupt(digitalPinToInterrupt(INT_PIN), INT_ISR, FALLING);

funciona como se espera (al menos con el core arduino-pico).

hola MaximoEsfuerzo, si, el procesador, como cualquier procesador distingue y reporta el tipo de flanco, en este caso uso el mbed y no distingue entre flanco ascendente y descendente, gracias por la aclaración!!

(posteo inicial corregido)

saludos

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.