Interrupciones delay () Tacometro

Buenas tardes,

Estoy intentando diseñar un tacómetro para medir diferentes velocidades. Para hacer las diferentes pruebas utilizo un ventilador de un viejo pc de 3 cables. He estado probando el PROGRAMA #1 y a la velocidad máxima del ventilador ningun problema, pero a velocidades bajas no funciona. Por otro lado he estado buscando y he encontrado otro PROGRAMA #2 en el que he hecho varias modificaciones. La principal es intentar evitar la funcion delay() para mostrar por pantalla el valor de la velocidad cada 1000ms. Pero no entiendo porque no funciona, alguna idea?

PROGRAMA #2

float Pulsos_fan=0;

int rpm;
unsigned long oldtime=0;
unsigned long oldtime2=0;
int temps;

void isr() //interrupt service routine
{
Pulsos_fan++;
}

void setup()
{
Serial.begin(9600);
attachInterrupt(0,isr,RISING);
}

void loop()
/*No entiendo porque no funciona la condicion if(oldtime>(oldtime2+1000)) y no imprime el valor rpm. La intencion de la condicion es evitar tener que usar la funcion delay(). */
{
delay(1000);
Serial.println(rpm);
detachInterrupt(0);      //      //detaches the interrupt
temps=millis()-oldtime;        //finds the time 
rpm=(Pulsos_fan/temps)*60000/2;         //calculates rpm
oldtime=millis();             //saves the current time
Pulsos_fan=0;
/*if(oldtime>(oldtime2+1000))
  {
    oldtime2=millis();
    Serial.println(rpm);
    Serial.println("Hola");
  }*/
attachInterrupt(0,isr,RISING);

}

PROGRAMA #1

const int PIN_hallsensor = 2; // Pin al que conectamos el sensor hal
unsigned int fan_type=2;   // Tipo de ventilador (nº de sensores hall)
volatile float Pulsos_fan=0; // Contador de pulsos, se declara 'volatile' pq se comparte entre la ISR y el programa principal
unsigned int T=250;        // tiempo de medida (ms)
//unsigned int speed_rpm;    // Resultado calculos en rpm
float speed_rpm;    // Resultado calculos en rpm

void setup() {
  pinMode(PIN_hallsensor, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(PIN_hallsensor), Count_Pulse, RISING); //interrupt is attached
}
 
void loop (){

  Pulsos_fan = 0; // Inicializa contador 
  sei();          // Habilita interrupciones
  delay (T);      // Tiempo de espera 0.5 seg
  cli();          // Deshabilita interrupciones
 
// Calculo de rpm en T/1000 segundos 
  speed_rpm = (Pulsos_fan *(1000/T)*60)/fan_type;
 
  Serial.print (speed_rpm, DEC);
  Serial.print (" rpm\r\n");
}
 
//============= Funcion ISR, a la que llama las interrupciones ====================

void Count_Pulse (){    
  Pulsos_fan++;  // Cuenta los pulsos que envia el ventilador
}

Hay muchas maneras de poder medir rpm de un motor, pero casi todas se basan en el mismo método: contar los pulsos en un intervalo de tiempo.

En tus dos programas hay una cosa que no me gusta nada, y es que usas float para medir los pulsos. ¿Por qué? Simplemente float es ineficiente en cuestión de tiempo. En estas situaciones hay que usar unsingned long.

En tu programa #1 utilizas sei() y cei() para habilitar las interrupciones y usas un delay de 250ms. He hecho simulaciones en proteus y a bajas velocidades funciona mejor que a altas revoluciones.

En cuanto al programa #2 no he querido analizar el código ya que el if era demasiado enrevesado cuando era más fácil hacerlo asi:

unsigned long  Pulsos_fan=0;
int rpm;
unsigned long oldtime=0;

void isr() //interrupt service routine
{
  Pulsos_fan++;
}

void setup()
{
  Serial.begin(9600);
  attachInterrupt(0,isr,RISING);
}

void loop()
{
  if ( millis()-oldtime > 1000 ) {
    detachInterrupt(0);
    // Calculo de las RPM...
    Serial.println(Pulsos_fan);
    Pulsos_fan=0;
    oldtime = millis();
    attachInterrupt(0,isr,RISING);
  }
}

En ambos códigos, aunque funcionan relativamente, veo que se pierden pulsos por la precisión de millis y del software añadido, con lo que se pierde precisión.

Existen librerias, que hacen uso de un TIMER hardware para realizar la operación, pero ahora mismo no caigo en la cuenta de ninguna. Generalmente opino que dependiendo de la velocidad es mejor medir tiempo entre pulsos usando micros(), que medir usando un tiempo fijo.