Hola chicos. Quiero compartir con vosotros un hecho curioso que me ocurre. Os pongo en contexto.
Hace ya un par de años que fabriqué un tacómetro para revoluciones lentas, además creo que discutimos un pequeño problema que había con el código en el post: Tacómetro y efecto no deseado en el código.
El tacómetro consiste en un display de 7 segmentos, cuatro botones y dos salidas a relé, de máxima y mínima velocidad. Dichas velocidades son programables y está pensado para una velocidad máxima de 500RPM. El sensor que utilizo es un sensor inductivo LJ12A3-4-Z/BX.
Se compone de dos placas: el panel con el display/botones y la placa con el atMega, fuente y conexiones.
He aquí la placa del panel y su esquema:
Y este el esquema del circuito principal:
El esquema de conexión es el siguiente:
El circuito funciona estupendamente y sin problemas, salvo algo que ocurre en algún punto entre Mayo y Julio. En esa época llega el monzón y hay tormentas a tutiplén y de golpe noto algo raro: cuando la máquina está parada, 0RPM, de vez en cuando, sin venir a cuento, sin haber nada raro, se produce un disparo por máxima velocidad!!
Me ha ocurrido ya dos años seguidos y lo he reparado obviamente. Cada vez que se avería compruebo que todo este funcionando correctamente: tensión de fuente, pantalla, botones, que el atMega responde y se deja programar.
Cuando reviso la entrada de pulsos proveniente del sensor reviso las tres cosas que hay: conector, resistencia y optoacoplador. De hecho no hay nada mas:
La alimentación de 12V del sensor es la misma que el Arduino reducida a 5V. Y es una conexión semi-aislada, es decir, comparte el GND aunque pasa por un optoacoplador, así solo hay que usar la resistencia PULL-UP.
Como se ve, es muy simple. Y después de analizar el problema siempre llego a la misma conclusión: el atMega falla, pero solo en el pin D2 (de interrupción) que se vuelve sensible de tal manera que cualquier alteración en el ambiente hace que provoque una interrupción.
Supongo que será que la descarga del rayo entra por GND y llega al pin 2. Curioso que solo en ese pin, ya que el resto de pines los he probado y siguen funcionando.
Programo otro atMega, lo pongo y a funcionar. Y el año que viene por las mismas fechas volver a repetir la operación.
No se me ocurre nada que proteja el pin: ¿varistor, zéner, TVS? Se supone que el atMega ya tiene diodos "clamp" que absorven picos, pero se ve que no son suficientes. ¿Alguna sugerencia? o estáis tan perplejos como yo.
Por si alguien quiere echar un vistazo al código lo dejo aquí, pero no es problema de software:
/*
TACOMETRO
Autor: VictorJAM.
Fecha: 22/03/2021.
*/
#include <SimpleButton.h>
#include <EEPROM.h>
#define RELEMINIMA 7
#define RELEMAXIMA 8
//--- VARIABLES CONTROL DISPLAY.
// Contiene los segmentos que se han encender para cada digito.
const uint8_t numeros[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66,
0x6d, 0x7d, 0x07, 0x7f, 0x67 };
// Pines donde están conectados los segmentos.
const uint8_t segmentos[7] = { A1, A3, 10, 11, 12, A2, 9 };
// Pines de control de cada digito.
const uint8_t digitos[4] = { 13, A0, 5, 6 };
uint8_t digitoActual;
uint32_t td;
//--- Botones.
SimpleButton btnSetMax(A5);
SimpleButton btnSetMin(A4);
SimpleButton btnUp(4);
SimpleButton btnDown(3);
//--- VARIABLES PARA LA MEDIDA DE PULSOS Y RPM
volatile uint32_t tanterior;
volatile uint32_t tactual;
volatile uint32_t periodo;
float f;
uint32_t rpm;
//--- VARIABLES DE CONTROL.
int maxrpm;
int minrpm;
uint32_t t1;
uint32_t t2;
uint32_t t3;
uint32_t t4;
uint32_t t5;
uint32_t t6;
//--- RUTINA DE SERVICIO A LA INTERRUPCIÓN.
void isr() {
tactual = micros();
periodo = tactual-tanterior;
tanterior = tactual;
}
// Ajusta un valor entre un valor minimo y un maximo. Si el valor 'value' es
// mayor que el de 'imax', 'value' será 'imin'. De la misma forma, si 'value'
// es menor de 'imin' su valor pasará a ser 'imax'.
void trimvalue(int &value, int imin, int imax) {
if ( value > imax )
value = imin;
else
if ( value < imin )
value = imax;
}
// Rutina de retraso al encendido. Su salida será uno cuando la condición sea
// cierta y haya transcurrido el tiempo indicado en duración; será cero en caso
// contrario.
unsigned int onDelayTimer(bool condicion, unsigned long &timer,
unsigned long duracion) {
if (condicion == false) { timer=0; return 0; }
else
if (timer == 0) { timer = millis(); return 0; }
else
if (millis() - timer >= duracion ) return 1;
else return 0;
}
// Dado un entero devuelve el valor decimal (o digito) especificado en la
// posición p.
int digitopos(int v, int p) {
int d, r;
d = v;
int i=0;
while (i<=p) {
r = d % 10;
d = d / 10;
i++;
}
return r;
}
// Muestra/enciende los leds correspondientes a ese digito en el display.
void printd(uint8_t v) {
if ( v>=0 && v<=9 ) {
for (int i=0; i<7; i++) {
digitalWrite(segmentos[i], bitRead(numeros[v], i));
}
}
}
// Muestra un digito de 4 cifras en un display de 4 dígitos. Usa la función
// delayMicroseconds. Útil para pruebas.
void actualizaDisplay1() {
for (int d=0; d<4; d++) {
printd(digitopos(valorDisplay, 3-d));
digitalWrite(digitos[d], HIGH);
delayMicroseconds(800);
digitalWrite(digitos[d], LOW);
delayMicroseconds(200);
}
}
// Muestra un digito de 4 cifras en un display de 4 digitos. Usa la función
// micros() con lo que no interrumpe el programa.
void actualizaDisplay2() {
if ( micros()-td > 2000 ) {
digitalWrite(digitos[digitoActual], LOW);
digitoActual++; if ( digitoActual==4 ) digitoActual = 0;
printd(digitopos(valorDisplay, 3-digitoActual));
digitalWrite(digitos[digitoActual], HIGH);
td = micros();
}
}
//--- CONFIGURACIÓN DE PROGRAMA.
void setup() {
// Establece los segmentos como salida.
for (int i=0; i<7; i++) {
pinMode(segmentos[i], OUTPUT);
}
// Establece los pines de control de digitos como salida.
for (int i=0; i<4; i++) {
pinMode(digitos[i], OUTPUT);
}
// Leemos las posiciones 0 y 2 que contienen un int con el valor por defecto
// de las variables minrpm y maxrpm.
EEPROM.get(0, minrpm);
EEPROM.get(2, maxrpm);
// Ponemos los pones de relé como salida.
pinMode(RELEMINIMA, OUTPUT); // RELE DE MINIMA
pinMode(RELEMAXIMA, OUTPUT); // RELE DE MAXIMA
digitalWrite(RELEMINIMA, 0);
digitalWrite(RELEMAXIMA, 0);
// Diodos de led indicadores de mínima y máxima.
pinMode(0, OUTPUT);
pinMode(1, OUTPUT);
// Pin de interrupcion, no usamos una resistencia de PULL-UP en el opto ya
// que podemos usar la interna.
pinMode(2, INPUT_PULLUP);
// IMPORTANTE!! La interrupción debe darse en el flanco de bajada.
attachInterrupt(digitalPinToInterrupt(2), isr, FALLING);
}
//--- PROGRAMA PRINCIPAL.
void loop() {
// Actualizo el estado de los botones.
btnSetMin.update();
btnSetMax.update();
btnUp.update();
btnDown.update();
// Rutina para que en el display se visualice cero, cuando hay pocas
// revoluciones (máquina parándose o arrancándose).
if ( periodo == 0 )
f=0;
else {
// Se guarda el tiempo actual y el valor de micros en sendas variables
// auxiliares. Esto reduce el error que se produce cuando intentamos leer
// las variables y se produce la interrupción, resultando que tactual
// tiene valor 0 y provocando que la frecuencia sea 0.
uint32_t a = tactual;
uint32_t b = micros();
if ( b-a > 1000000UL) {
f=0;
}
else {
f = 1000000.0 / periodo;
}
}
// La frecuencia se expresa en Hertzios, para convertirla a r.p.m. debemos
// multiplicar por 60. Despues redondeamos el número obtenido.
f = f*60.0;
rpm = round(f);
// Para activar los relés tenemos que tener en cuenta que la rutina no es
// perfecta y da errores dando como resultado que la frecuencia es cero, aun
// cuando es fija. Para ello se usa la función 'onDelayTimer' tanto en el
// el encendido como en el activado.
// Relé/Led de mínima.
digitalWrite(RELEMINIMA, onDelayTimer(rpm<minrpm, t1, 500));
digitalWrite(1, !onDelayTimer(rpm<minrpm, t3, 20));
// Relé/Led de máxima.
if ( onDelayTimer(rpm>=maxrpm, t2, 500) )
digitalWrite(RELEMAXIMA, HIGH);
else
if ( onDelayTimer(rpm<maxrpm, t5, 100 ) )
digitalWrite(RELEMAXIMA, LOW);
if ( onDelayTimer(rpm>=maxrpm, t4, 20) )
digitalWrite(0, LOW);
else
if ( onDelayTimer(rpm<maxrpm, t6, 20) )
digitalWrite(0, HIGH);
// Pequeño menú para interactuar con el display y poder elegir el número de
// rpm de mínima y de máxima.
if ( btnSetMin==Pressed ) {
valorDisplay = minrpm;
if ( btnUp==Press ) { minrpm++; trimvalue(minrpm, 0, 500); }
if ( btnDown==Press ) { minrpm--; trimvalue(minrpm, 0, 500); }
}
else
if ( btnSetMax==Pressed ) {
valorDisplay = maxrpm;
if ( btnUp==Press ) { maxrpm++; trimvalue(maxrpm, 0, 500); }
if ( btnDown==Press ) { maxrpm--; trimvalue(maxrpm, 0, 500); }
}
else
valorDisplay = rpm;
if ( btnSetMin==Release ) { EEPROM.put(0, minrpm); }
if ( btnSetMax==Release ) { EEPROM.put(2, maxrpm); }
// Muestra el digito.
actualizaDisplay2();
}









