Interrupciones y variables

Ben dia.
El problema que me atañe ahora es.
Usando arduino uno pretendo recibir dos señales (Voltaje) que no deben perderse y las recibo por las interrupciones 0 y 1
Ahora bien, cada señal debe ser procesada en arduino y enviada por puerto serial, así como vendrán datos por el puerto serial que alteraran las variables que se modifican en las interrupciones
Llamemos var_0 a la variable que se incrementara en una unidad cuando exista una interrupción en el pin2 y nos olvidamos de la otra interrupción.
El programa tiene un loop que captura el puerto serial y envía el valor que tenga en var_0 y luego le asigna el valor 0 para resetearla.
Mi problema es. Como impido el acceso a esa variable cuando justo llega una interrupción al momento de la escritura var_0 = 0.
ya que puede darse el siguiente caso
Estado inicial de la variable
var_0 = 5;

  1. Lee el valor de la variable var_0 y envía por puerto serial ("var=5")
  2. llega una interrupción y ejecuta var_0++;
  3. Retorna al loop y ejecuta var_0 = 0;

Para este momento he perdido un valor ya que se envio 5 pero llego otra señal mas pero esta se perdio al hacer qu ela variable se haga cero.
Puedo impedir que eso suceda, que tipo de variable me conviene usar para trabajar en interrupciones volatile?
Gracias por su ayuda...

Desde ya que cualquier variable que se modifique dentro de una interrupción debe ser volatile.

Bueno, has llegado al punto por el cual no es tan fácil como parece trabajar con interrupciones.
El mayor problema es que cambias el valor de la variable desde diferentes fuentes (la interrupción y el serial) y que se pueden dar simultáneamente. Te lo has puesto bien difícil.

Las opciones que se me ocurren son, bloquear la interrupción hasta salir de la rutina de comunicación serial (si es posible) o bien trabajar con otra variable que copie el valor de var_0 y trabajar con esa cuando la envias y modificas (llegado el caso, puedes usar una variable flag que indique que var_0 fue modificado por la interrupción), finalmente volcar el valor de esa variable nuevamente a var_0 (si no fue modificada).

Sinceramente no se me ocurre una situación como la que presentas pero buscaría otro camino para no llegar a ese punto. :wink:

Saludos

Esto no es posible porque el mismo dice

Entonces descartada la opción pero esta bueno enumerarla.

A mi se me ocurre en parte lo 2da opción de @anon90500195. Pensé en un flag pero que en lugar de ser borrada en el loop sea en la interrupción si dicho flag esta activo.
Digamos que llamo al flag borrar_var_0 si esta en true lo borro sino sigo.
Como no pierdo nada?
Pues en la interrupción al comienzo lo chequeo, si esta en true borro var_0 y sino sigo y lo incremento. Me parece mas simple.

// ISR pin 2, disminuye la velocidad
void interrupcionPin2() {
if (borrar_var_0)
    var_0 = 0;
var_0++;

}

1 Like

Como decía un viejo profesor de Análisis de Sistemas:
Siempre hay otra opción pero simplemente no la estamos visualizando.

Igual no me queda claro como se evitaría perder al menos una medición y finalmente qué diferencia habría con bloquear la interrupción. :thinking:

Saludos

No hay soluciones mágicas.
No digo que mi aporte sea la solución. Yo solo me aparto de lo que genera el puerto serie en cuanto a lentitud y que lo resuelva la interrupción. Si se pierde 1 bueno... es el costo posible.

Tal vez la idea de otra variable secundaria que contemple esa posibilidad y que sirva para comparar luego de que sucedan todos los eventos.
Si las variables son iguales no se hace nada y si son distintas es porque entró otro evento (interrupción) y entonces se le da curso.

Y eso me hace pensar porque se pone a 0? porque no simplemente se sigue incrementando y solo capturamos su valor y hacemos la diferencia al momento de enviarlo, total sigue siendo una acción lenta, enviar por serial.
La interrupción solo hace incrementar el contador aunque se desborde no pasa nada, mismo caso que millis().
Por otro lado al momento de enviar, lo hago es enviar la diferencia entre el valor actual y el valor anterior. Y listo, creo que es mas rapido y mas limpio.

@Surbyte No fue una crítica, no lo tomes así que lejos de eso estuvo mi comentario.

Justamente porque no hay soluciones mágicas es que me planteé (y tal vez hice mal en compartir el pensamiento) que borrar el contador o ignorar la interrupción finalmente genera el mismo resultado (a menos que algo no esté visualizando, como diría el profe).

Para variar faltan datos como para evaluar la prioridad de la interrupción, no sabemos qué la genera, por ejemplo. No sabemos si tiene determinada periodicidad o es aleatoria. Etc., etc..

Saludos

Para nada @anon90500195 tomé tu comentario como crítica. Estamos debatiendo ideas y tu tienes mejor formación que yo en programación.
Yo creo que la idea de no poner a cero (estilo millis()) es lo mejor. Yo lo haría asi y probaría. O sea una variable que no cambie y solo se incrementa con cada INT y captura su estado actual menos el anterior para enviar el dato.

Si, comparto totalmente ese enfoque.

Lo de no llevar a cero la variable tiene sentido pero, ¿No es mas costoso hacer la resta y guardar esoi en otra variable? y luego hay que validar el desbordamiento.
No especifiqué periodicidad en las interrupciones porque son sumamente aleatorias tanto para el envío de la info a la pc así como las validaciones de comunicación.
Inicialmente pensé en encapsular la variable en una clase con sus getters y setters entonces en la funcion set se evalua si la variable esta siendo usada por otro recurso y manejar punteros a funciones para controlar lo que debe suceder si la variable esta en uso pero.
Y el gran pero.
Arduino es mono núcleo (un solo proceso logico). entonces si en interrupción toco variable, el código externo no va a poder hacer nada tampoco entonces no se va a llegar nunca al caso de dos procesos accediendo al mismo tiempo a una misma variable ¡O si?
una función al inicio podría capturar el valor de la variable y trabajar sobre ese valor asi no se alteran los cálculos o proceso dentro de esa funcion por una interrupcion salvaje qu eaparezca mientras de lleva a cabo el proceso de la función en mención.
Dicha funcion seria la que transmite los datos a la PC.
DOs preguntas.
La comunicación serial se detiene al entrar en interrupción el micro ¿La info que se envía se podría corromper por no completar una linea de ejecución de código por la interrupción?.
¿Se puede acceder a un método de comunicación mas veloz y directo con arduino uno y la pc?
Gracias por ayudarme

No, porque no habría desbordamiento.

Costoso en tiempo? Ya de por si enviar datos por serial insume tiempo pero que remedio? Sube la velocidad a 115k2 bps.
Si es mas o menos costoso debes determinarlo tu mismo, haciendo mediciones con micros() por ejemplo. Busca y analiza que es mas rapido. Cual de las opciones se desenvuelve mejor.
Yo simplemente aportaba una idea para no perder cambios. No digo que tenga cubiertas todas las aristas ni que sea la mejor idea.

No, el micro tiene una (o más) UART, que maneja la comunicación de forma independiente.

Por supuesto hablo de los puertos seriales hardware, SoftwareSerial es otro tema.

Para este tipo de dudas te recomiendo que siempre recurras a la hoja de datos del micro que estés utilizando.

Asumiendo que el int fuera un byte al llegar a 125 este ya no seria 125 sino -124
Resto 124 -(-124) = 248 cosa que tambien esta desbordada.

Primer error de concepto, el tipo byte no tiene signo, entonces si la variable contiene 255 al incrementar pasa a contener 0, no hay desborde.

Tu ejemplo aplicaría para el tipo char que equivale a byte con signo, con el detalle que el rango numérico va de -128 a 127.

Volvamos al byte, a 4 le restamos 240,

4 - 240 = - 236

Pensaríamos que está mal pero como el byte no tiene signo el resultado real es 20.
Y por supuesto, no hay desborde.

Saludos

Por eso mismo lo sugerí, porque es la misma operación que hacemos con millis() o sea un entero sin signo de 32bits. No hay desborde. Las cuentas se mantienen correctas.
Si algo hace bien un compilador es sumar o restar. Tambien poner a 0.
En lugar de congeturar porque no hacemos una prueba real.
Dices que serán interrupciones aleatorias.
Podriamos usar un arduino como generador aleatorio y otro que haga las cuentas y ver si te pierdes de algo. Ahora que tan aleatorio puede ser como para hacer fallar el envio de datos?
Supongamos que usas 115200 bps y enviaras solo el número por el método que sea.
A 115k2 tienes 11520 bytes/seg supongamos 8N1 como siempre o sea 10 bits x trama te esa tasa de bytes.
11520 bytes .......... 1 seg
cuantos bytes enviaras por puerto serie? supongo 3 digitos o sea 3 bytes mas extras (2) digamos 5?

11520 bytes .... 1 seg
5 bytes ........ X = 5/11520 seg = 434 useg
Mas resto del código, supongamos 1.5 mseg por dar un valor.
En ese tiempo ocurren interrupciones, cuantas? Muchas, pocas ? Bueno no se va a perder ni 1 sola. Como la interrupcion es un código muy corto, va suma 1 a la variable y vuelve. Ni cuenta se da el puerto serie.

Caso 1: Estaba por imprimir 12 (como diferencia) Valor actual 32. Valor anterior 20 y ocurre una nueva, o sea Valor actual 33, pues imprimirá 13.

Caso 2: Imprimió 12 y ocurre una nueva luego de calcular la diferencia pues dependerá si hizo la diferencia y luego capturaste el valor anterior o no.

Si hizo la diferencia el unico problema es que el valor anterior ahora no refleja lo real pero por cuanto tiempo? por lo que le toma asignar el valor anterior.
Busca cuanto demora en ciclos reloj asignar un valor en ram y ahi tienes tu resolución.

Si el byte no tiene signo peor el int32 si pero igual se puede usar uint, era solo como ejemplo ya que igual tienes 255-0= 255, cuando por desbordamiento saltas 255 a 256 que seria el siguiente.

A los tiempos que entro y aun sigue Surbyte aqui. Graande
A lo que hacia referencia es a sumar mas retraso al que de por si tenga la comunicación serial.
Lo que no puse es que la interrupción solo incrementa la variable pero en loop se procesa el dato, en principio tenia miedo que la variable se corrompiera por acceso desde dos puntos al mismo tiempo.
Aun no esta solucionado porque el hardware del proyecto que hago presento problemas y los tengo que solucionar.
(Este mensaje no se fue cuando debía, me quede sin internet)

Si tratase esta consulta por los tiempos en los que se da las interrupciones, no tendría problema ya que arduino es mas rápido que las interrupciones para este proyecto en particular.
Probablemente un espacio de 150 ms entre cada interrupción como tiempo mínimo.
Pero mi objetivo es tratar de hacer el código lo las general para luego en el futuro regresar a mi consulta y reutilizar código y conocimiento.
Al quitar de la ecuación el tiempo entre interrupciones, esto podría ser aplicado para cualquier proyecto pero creo que lo dejare asi nomas porque el proyecto tiene que continuar.
Muchas gracias por los aportes que me ayudaron a ver cosas que no estaba viendo como el hecho de no hacer el reset a 0 sino solo restar aunque igual no he evaluado los pros y contras con señales mas rápidas. mas después estaré por el foro preguntando o ayudando si es que puedo

#define IZQ 4
#define DER 7
const int interpin = 3;

char Char = '\0';
String Var_NomVar = "";
String Var_ValVar = "";
int Var_ReceptionState = "";

volatile int val_inter = 0;

volatile int aux_inter = 0;

volatile int inter = 0;

volatile int valida_inter = 0;


void _inter(){
  val_inter++;
}

void setup() {
  attachInterrupt(
    digitalPinToInterrupt(interpin),
    _inter,
    RISING);

  Serial.begin(115200);
  Serial.println(F("Hola."));
  
  pinMode(IZQ,OUTPUT);
  pinMode(DER,OUTPUT);
}


void envia_inter(){
  if(val_inter > 0){
    if(aux_inter == 0){
      aux_inter = val_inter;
      Serial.print(aux_inter);
      val_inter = val_inter - aux_inter;
      aux_inter = 0;//Esto se supone validara la respuesta recibida por el PC
    }
  }
}


void com(){
  if (Serial.available() > 0){
    
  }
}

void loop() {
  envia_inter();
  com();
}

Aquí mi código incompleto pero código en fin

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