desktop

Arduino ESP32 y su sistema de interrupciones.

@cosmefulanito04 : he probado por polling y va perfecto, ni rebotes ni filtros ni falsos nada. Cuenta 100, alguna vez 101 y alguna 99 pero una de cada bastantes. Va mejor que el mejor filtrado de las interrupciones.

Supongo que si waitTime tiene un valor bajo, digamos 1, podría darse que al llegar al estado SET_LOW_TRC_STATE, el DPC_PIN siga en alto y generes un falso disparo. Se podría agregar un estado más que verifique que el DPC_PIN vuelva al estado bajo, pero esto puede traer problemas si el waitTime es grande, digamos que 99/100. Probaría con esto:

C:
...
#define IDLE_STATE                            0
#define DPC_FIRST_CHECK_STATE                1
#define SET_HIGH_TRC_STATE                    2
#define SET_LOW_TRC_STATE                    3
#define WAIT_STATE                            4
 
#define DPC_PIN DO
#define TRC_PIN D1

#define MAX_WAIT_TIME                        50

void timer(){
 
    switch(stateVar)
    {
        case DPC_FIRST_CHECK_STATE:
            if(digitalRead(D0) > 0)
            {
                // First check pass after 100uS later
                stateVar = SET_HIGH_TRC_STATE;
            }
            else
            {
                // Bounce or noise
                stateVar = IDLE_STATE;
                FlexiTimer2::stop();
            }
            break;

        case SET_HIGH_TRC_STATE:
            if(digitalRead(DPC_PIN) > 0)
            {
                // Second check pass after 200uS later
                stateVar = SET_LOW_TRC_STATE;
            
                digitalWrite(TRC_PIN, HIGH);
                FlexiTimer2::stop();
                FlexiTimer2::set(waitTime, 1.0/10000, timer); // Set waitTime*100uS "tick"
                FlexiTimer2::start();
            }
            else
            {
                // Bounce or noise
                stateVar = IDLE_STATE;
                FlexiTimer2::stop();
            }
            break;

        case SET_LOW_TRC_STATE:
            // Clear TRC PIN  after waitTime*100uS later
            stateVar = WAIT_STATE;
        
            digitalWrite(TRC_PIN, LOW);
            FlexiTimer2::stop();
            FlexiTimer2::set(1, 1.0/10000, timer); // Set 100uS "tick"
            FlexiTimer2::start();
            break;

        case WAIT_STATE:
            if((waitTime > MAX_WAIT_TIME) || (digitalRead(DPC_PIN) < 1))
            {
                stateVar = IDLE_STATE;
                FlexiTimer2::stop();
            }
            break;

        default:
            // Wrong state!
            stateVar = IDLE_STATE;
            FlexiTimer2::stop();
    }
}
...

Con MAX_WAIT_TIME, se define que si waitTime es menor (o igual) a 5mS (50), se habilite la espera en el estado "WAIT_STATE" hasta que el puerto DPC_PIN vuelva a 0. Si waitTime tiene un valor alto (más de 5mS), ya no hay necesidad de esperar que el puerto DPC_PIN vuelva a 0.

De todas formas habría que ver que pasa cuando waitTime sea 100 o cercano (10mS), si se pierde un pulso o no. Para evitar eso, se podría quitar el doble chequeo inicial "DPC_FIRST_CHECK_STATE", pasando directamente del estado "IDLE_STATE" al estado "SET_HIGH_TRC_STATE" y reducir la escala waitTime hasta 98 o 99.
 
Última edición:
Acá les dejo el código fuente de gestión de los GPIO. Tiene las funciones mapeadas a los nombres que usa Arduino, pero internamente son bastante mas complicadas aunque el código se entiende fácil. Esto es de la ultima versión de Espressif que está en git-hub.

@Scooter , que versión tenés instalada (en el IDE Arduino) de la biblioteca de los ESP32 ?? Por que me resulta extremadamente rebuscado meter una máquina de estados mas un timer para zafar de interrupciones repetidas. Me inclino mas por algún problema con versiones de software o compatibilidad de micros, pero no por un hardware fallado de fábrica...
 

Adjuntos

  • fuentes.zip
    4.1 KB · Visitas: 4
Para el caso el mismo efecto tiene.
Si expressive hace mal el hardware o el software el caso es que funciona mal.

Al final lo voy a dejar así que parece el justiprecio a este follón:
C:
/*
Esp32 detección de paso por cero
*/

#define entrada D0
#define salida D2

static unsigned long hora=0;
static unsigned int ISRs=0;
static int pCero = 0;    //Cuenta de pasos por cero

void IRAM_ATTR interr(){
disableInterrupt(entrada);
 if (micros()-hora>70){
    digitalWrite(salida,HIGH);
    pCero++;
    digitalWrite(salida,LOW);
    hora = micros();
  }
ISRs++;
enableInterrupt(entrada);

}

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Detección de paso por cero");
pinMode(entrada,INPUT_PULLUP);
pinMode(salida,OUTPUT);
attachInterrupt(D0,interr,RISING);    // En el ESP32 hagas lo que hagas solo va HIGH

}

void loop() {
  // put your main code here, to run repeatedly:
Serial.print("Pasos por cero :   ");
Serial.print(pCero);
pCero = 0;
Serial.print("     ISRs realizadas =");
Serial.println(ISRs);
ISRs=0;
delay(1000);
}

Esto está dando este resultado que sin ser una joya es aceptable:
Código:
Detección de paso por cero
Pasos por cero :   0     ISRs realizadas =0     <<<Aquí está sin enchufar a la red
Pasos por cero :   0     ISRs realizadas =0
Pasos por cero :   0     ISRs realizadas =0
Pasos por cero :   0     ISRs realizadas =0
Pasos por cero :   0     ISRs realizadas =0
Pasos por cero :   193     ISRs realizadas =239  <<<Se enchufa en este momento; chisporroteos
Pasos por cero :   202     ISRs realizadas =207  <<< Tiene que dar 200 porque lee los pulsos de subida y bajada
Pasos por cero :   200     ISRs realizadas =211
Pasos por cero :   200     ISRs realizadas =206
Pasos por cero :   200     ISRs realizadas =214
Pasos por cero :   202     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =206
Pasos por cero :   202     ISRs realizadas =206
Pasos por cero :   200     ISRs realizadas =208
Pasos por cero :   200     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =207
Pasos por cero :   202     ISRs realizadas =211
Pasos por cero :   200     ISRs realizadas =204
Pasos por cero :   201     ISRs realizadas =207
Pasos por cero :   201     ISRs realizadas =207
Pasos por cero :   200     ISRs realizadas =204
Pasos por cero :   200     ISRs realizadas =209
Pasos por cero :   202     ISRs realizadas =208
Pasos por cero :   200     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =211

Para obtener 200 se hacen 206~211 interrupciones que no es una exageración.
201 o 202 puede ser válido porque en 1s entran 100 ciclos pero podría coincidir 101 transiciones, podría ser que el bucle tarde un poco mas de 1000ms, no me parece fantástico pero si aceptable.

Sin el enable y disable interrupt saca como 3000 ISRs realizadas
 
Sin el enable y disable interrupt saca como 3000 ISRs realizadas
Si, eso se vé en el código fuente que subí. En ninguna parte toca un registro de interrupciones ni las deshabilita...
El alguna parte del datasheet del C3 dice que se puede activar una especie filtrado por hardware para eliminar pulsos parásitos en la línea de entrada y sincronizarla con el reloj del micro....cosa raaaara....
 
Scooter, solo por asegurar: has probado a que las variables de la interrupción sean volátiles?
Me acabé liando y las declaré static en lugar de volatile. Las he puesto volátiles pero es indiferente el resultado.
Te confirmo que el resultado es el mismo:
C:
/*
Esp32 detección de paso por cero
*/

#define entrada D0
#define salida D2

volatile unsigned long hora=0;
volatile unsigned int ISRs=0;
volatile unsigned int pCero = 0;    //Cuenta de pasos por cero

void IRAM_ATTR interr(){
disableInterrupt(entrada);
 if (micros()-hora>70){
    digitalWrite(salida,HIGH);
    pCero++;
    digitalWrite(salida,LOW);
    hora = micros();
  }
ISRs++;
enableInterrupt(entrada);
}

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Detección de paso por cero");
pinMode(entrada,INPUT_PULLUP);
pinMode(salida,OUTPUT);
attachInterrupt(D0,interr,RISING);    // En el ESP32 hagas lo que hagas va CHANGE
}

void loop() {
  // put your main code here, to run repeatedly:
Serial.print("Pasos por cero :   ");
Serial.print(pCero);
pCero = 0;
Serial.print("     ISRs realizadas =");
Serial.println(ISRs);
ISRs=0;
delay(1000);
}
Resultado:
Código:
Pasos por cero :   202     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =214
Pasos por cero :   200     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =201
Pasos por cero :   202     ISRs realizadas =220
Pasos por cero :   200     ISRs realizadas =200
Pasos por cero :   200     ISRs realizadas =206
Pasos por cero :   200     ISRs realizadas =206
Pasos por cero :   202     ISRs realizadas =211
Pasos por cero :   200     ISRs realizadas =201
Pasos por cero :   202     ISRs realizadas =209
Pasos por cero :   200     ISRs realizadas =205
Pasos por cero :   200     ISRs realizadas =201
Pasos por cero :   200     ISRs realizadas =209
Pasos por cero :   202     ISRs realizadas =209
Pasos por cero :   200     ISRs realizadas =206
Pasos por cero :   200     ISRs realizadas =203

No obstante las dejo volatile que es "como debería de ser". Static en variables globales no tiene mucho sentido.
 
Última edición:
Con el esquema del primer post un PC814 y una resistencia de 150k.
Lo tengo montado en varias placas y en varias "posturas", en todas hace lo mismo.

He actualizado el código del dimmer en el hilo del dimmer.
Al final he añadido unos matices mas.
Funciona razonablemente , aunque no fino del todo en ángulos de disparo muy grandes, probablemente necesite mas anchura de pulso de disparo pero no he querido hacerlo muy grande.
 
Buenas. Espero no estar reviviendo muertos...

Me han funcionado a la primera las interrupciones. He puesto un contador en la interrupción y cuenta de uno en uno, bien. Lo he hecho todo igual que Scooter en su mensaje #27, salvo esta línea:
attachInterrupt(D0,interr,RISING); // En el ESP32 hagas lo que hagas va CHANGE
que yo he puesto:
C:
attachInterrupt (digitalPinToInterrupt(DO), interr, RISING);

Ya me dirás si con ese cambio te funciona
 
Debería de dar lo mismo, la función solo transforma el nombre del pin al nombre de la interrupción pero en el xiao se llaman igual.
Lo probaré, no se pierde nada.

Al final funcionó bastante bien, es una combinación de factores. El procesador es mucho más rápido y detecta cambios que otros no detectan. La impedancia importa, en la entrada del resolver la resistencia es de 2k2 y ahí va bastante mejor, no del todo, algo de porquería entra.
 
Muy buenas:
Trasteando con Arduino IDE (v.2.3.2) para programar un ESP32, he activado las alertas de compilación desde archivo/preferencias, y he probado a compilar. Compila y funciona bien, pero ahora saca un mensaje que dice:
Código:
In function 'void handleInterrupt()':
warning: '++' expression of 'volatile'-qualified type is deprecated [-Wvolatile]
Y es que tengo, como tú, una expresión "variableVolatil++;" dentro de la función interrupción. Por lo visto, y no sé si será cierto..:
Código:
El problema es que la operación ++ no es segura para variables volatile
porque la operación de incremento puede implicar múltiples pasos (lectura, incremento, y escritura),
 y el compilador no garantiza que la variable no cambie en medio de estos pasos.
En consecuencia, usar ++ directamente con variables volatile puede llevar a resultados inesperados o a comportamientos indeseados.

En fín, es poco probable que sea la causa, porque en mí caso, la variable, cada interrupción sube una unidad correctamente.
He solucionado el warning cambiando el "variableVolatil++" por:
Código:
  int temp = variableVolatil;  // Leer el valor y lo almacena en variable local temporal
  temp++;              // Incrementar el valor temporal
  variableVolatil= temp;

Un saludo a todos
 
Última edición:
RISING, FALLING o CHANGEn es indiferente, hace un tren de pulsos mientras la señal sube y mientras la señal baja
Buenas:
Os cuento: Estoy teniendo el mismo problema! Hace 3 mensajes dije que me funcionaba bien, pero he detectado un caso en el que no, y me hace lo mismo que a Scooter.

Tengo un programa muy simple que activa una salida, tras esperar 1000ms, una vez saltada una interrupción por flanco de subida.

Con las pruebas que he hecho, me da la sensación de que si el flanco es digital, entonces funciona bien, pero si tiene "un poco" de curva, y ya sé que "un poco" es muy relativo, salta indiferentemente por subida o por bajada.

Mejor unas imágenes:

CASO 1. FUNCIONA BIEN
Esto es lo que tiene que hacer. No tiene que activar la salida si hay un flanco de bajada, porque he configurado la interrupción por flanco de subida:
BIEN.jpg
Se puede apreciar que el flanco de bajada cae bruscamente. Abajo a la izquierda tenéis la escala de tiempo: 100ms/div.

Os pongo un zoom del flanco que interpreta bien el ESP32:
FLANCO DIGITAL.jpg

Vale, ahora viene el caso 2, que me activa la salida cuando no debería.
CASO 2. FUNCIONA MAL. DETECTA EL FLANCO QUE NO DEBERÍA, Y ACTIVA LA SALIDA AZUL
MAL.jpg

Os muestro un zoom del flanco malo. Ya véis que no tiene ruido. De hecho tengo un filtro pasa bajos. Aunque me acerque más no se ve ruido significativo:
FLANCO ANALÓGICO.jpg

Bueno, de momento ésto es lo que tengo. Espero poder solucionarlo con un trigger schmitt... ya os iré contando.

A qué puede deberse ésto? Quizás sí que sea tema API. Estoy usando visual studio code, con librería Arduino
 
Última edición:
Lo que aprendí es que es muy pero que muy sensible alos ruidos, si fluctua un poco lo mismo te entran tres que seis. Bajando la impedancia mejora mucho, haciendo todo el lío que hice también aunque no va al 100% limpio.
Habrá que poner un circuito con un pasabajos y un trigger para filtrar porquerías.

Vamos que el circuito típico de una resistencia y un optoacoplador da problemas. Muchos problemas. Hay optoacopladores con salida por puerta lógica, habrá que probar uno de esos, pero ya es ir comprando "cosas raras". Yo era feliz con un PC814, una resistencia y la interna de pullup, pero ya no, no soy feliz.
 
Me da a mí que es un problema totalmente normal de cualquier microcontrolador que no tiene incluidos schmitt trigger en las entradas (como es el caso del ESP32), por pasar demasiado tiempo en la zona de tensión indeterminada, y empieza a leer 0s y 1s aleatoriamente. El ESP32 es bastante rápido, no me extrañaría nada.

Me refiero a la típica tabla:
1727765039291.png

Y aquí los valores específicos del ESP32:
1727764910224.png
 
Última edición:
Exacto, por eso empeora si le pones un RC simple como filtro pasabajo.
Si le pones un filtro antes de un trigger seguramente mejorará.

Además con una CPU de 160MHz en el rato de esa indefinición tiene tiempo para hace muchas tonterías.
Si fuese de 16 haría 10 veces menos chorradas .
 
Atrás
Arriba