Hola a todos, os pongo en situación.
Realicé un tacometro con un display 1602, un sensor óptico ranurado y dos relés
de salida: uno para mínima velocidad y otro para máxima. Las vueltas son pocas,
en regimen de trabajo son 375 rpm y los reles se activan a 350 y 380 rpm. En
teoría está trabajando perfectamente ya unos años.
Pero ahora, me encontré con la necesidad de hacer uno nuevo. En este caso con las
mismas características, pero sustityendo la pantalla de cristal liquido por un
display de 7 segmentos; entre otras cosas para que se vea a distancia.
Así que hice el diseño de la PCB en dos partes. Por un lado el display, botones
para interfaz y un par de leds indicadores. Por otro lado la placa con un atmega328p,
la entrada del sensor, fuente y salidas de relé. Monté la placa y me puse a hacer
pruebas.
Aquí empieza la aventura.
Conectas el tacómetro y parece que bien, pero de vez en cuando el led indicador de
mínima se enciende, subes las rpm y la cosa empeora, a tal punto que cuando está
por encima del máximo se vuelven locos los reles. La cosa fue a peor. Para ver que
relé es el que se enciende, coloqué un led en sus contactos que se enciende al
cerrarlos. Más loco aún.
De momento en el hardware he llegado a la conclusión de que tengo un mal diseño de
placa y alguna línea esta cerca de algo que no debería. No es lógico que un simple
led te provoque un problema gordo. Toca hacerlo de nuevo.
Pero claro, antes hice pruebas de software por si en algún sitio habia metido la
pata. Así que armé en la protoboard un display, un adaptador de señal para el generador
de funciones y el arduino para ver que ocurre, he hice el siguiente programa:
uint32_t tactual; // Coge el tiempo en el que se produce un flanco.
uint32_t tanterior; // Guardará el tiempo actual para poder restar en el
// siguiente flanco.
uint32_t periodo; // Tiempo entre flancos.
float frecuencia; // La inversa del periodo
// Se ejecuta en cada flanco ascendente de la señal, leyendo el momento en el
// que se produce con micros y restando el tiempo transcurrido desde el pulso
// anterior obteniendo el periodo.
void rutinaInterrupcion() {
tactual = micros();
periodo = tactual - tanterior;
tanterior = tactual;
}
// Configuración.
void setup() {
Serial.begin(9600);
// Usamos el pin 2 que se corresponde con la interrupción 0, asi que lo
// configuramos con entrada y aprovechamos la resistencia interna de pull-up
pinMode(2, INPUT_PULLUP);
// Ajustamos el vector de interrupción a nuestra función.
attachInterrupt(0, rutinaInterrupcion, RISING);
}
// loop.
void loop() {
// Para evitar dividir por cero comprobamos que el periodo no sea 0.
if ( periodo == 0 ) frecuencia = 0;
// Cuando la frecuencia es muy baja no se produciran interrupciones y puede
// que el periodo se quede fijo, por eso observamos micros y la ultima vez
// que lo guardamos, si el tiempo es muy alto lo ponemos a 0. Si no
// calculamos la inversa del periodo para obtener la frecuencia.
if ( micros()-tactual > 1000000 ) frecuencia = 0;
else
// Importante!! Ese millon debe ser un float, si ponemos un entero hará
// una división entera y nos truncará la frecuencia.
frecuencia = 1000000.0 / periodo;
// A partir de aquí, podemos mostrar la información en un display, serie, y
// realizar acciones con el código.
if ( frecuencia == 0 ) {
Serial.println(F("*"));
}
}
Cómo veis el código es muy simple.
Por un lado la funcion rutinaInterrupción, lo unico que hace es medir el tiempo
que ha transcurrido entre pulsos, guardando el valor en la variable periodo. He
medido la distancia entre pulsos, ya que si la maquina gira a 375 rpm, nos da una
frecuencia de 6.25Hz.
Por otro lado en el código, para obtener la frecuencia, hago la inversa y para
obtener hertzios utilizo 1000000.0. Hasta ahi bien.
¿Cómo controlo cuando la máquina va muy despacio? En el loop se compara el tiempo
transcurrido desde que se actualizó la variable, si es superior a un tiempo se
pone la frecuencia a cero. Si no hiciera esto cuando la máquina este parada en el
display se mostrará la última velocidad y no cero.
El código funciona bien y tengo una precisión de 0.01 Hz que casi no se nota. Pero
ocurre algo misterioso que no llego a comprender bien y es donde haber si podéis
iluminarme.
Exporadicamente la condición:
if ( micros()-tactual > 1000000 ) frecuencia=0;
Se cumple y entonces se imprime el "*" por el puerto serie. Tened en cuenta que
ocurre a regimenes de frecuencia fijos donde el periodo es mucho menor a ese millón
de microsegundos.
Cosas:
-
El generador de señales es chinesco y dudo mucho que sea preciso a altas frecuencias,
pero hasta 100Hz si lo es. -
La salida del generador es una señal cuadrada de 20V de amplitud que ataca el led de
un optoacoplador. La salida de dicho optoacoplador se conecta al pin D2 usando la
resistencia interna de PULL-UP, para pruebas sin condesandor de filtro. -
El tiempo entre "*" es también un poco aleatorio, no parece ser constante, si parece
que se ve afectado por la frecuencia. -
En el ejemplo no he declarado las variables de la rutina como "volatile", pero también
lo he probado obteniendo el mismo resultado. -
Para frecuencias pequeñas creo que es mejor medir distancia entre pulsos. Podría
medir número de pulsos en un intervalo de tiempo, pero para obtener precisión debería
tener mas sensores o un tiempo de intervalo muy alto. -
En el código de ejemplo he quitado el código del display, dado que no es importante
para la comprensión del código y lo único que hace es mostrar la frecuencia.
Supongo que el problema debe venir de alguna situación donde se produce la interrupción
justo cuando se hace la comparación, aún así, no logro ver como puede afectar.
PD. luego subo fotos y esquemas del circuito.