#define enable 5
#define up_buton 34
#define down_buton 25
#define signal_in 22
#define signal_out 19
hw_timer_t *timer = NULL; //configuración del timer.
hw_timer_t *timer_b = NULL;
hw_timer_t *timer_c = NULL;
volatile int count_finish=0;
volatile int antibounce=1;
volatile int semiperiod=0;
void IRAM_ATTR timer_end(){ //Timer para el tiempo de medida.
count_finish=1;
}
void IRAM_ATTR timer_b_end(){ //Este timer actualiza el valor de antibounce pasados tk ms.
antibounce=1;
}
void IRAM_ATTR timer_c_end(){ //Timer para generar la función
semiperiod=1;
}
//INTERRUPCIONES//
#define tk 200 //delay para evitar rebotes en los pulsadores (ms).
volatile int enable_true=0; //Variable que habilita o deshabilita la señal variable.
void IRAM_ATTR ISR_enable(){ //ISR que habilita o deshabilita la señal.
if(antibounce) {
enable_true=!enable_true; //Cambia el valor de la variable de 0 a 1 y viceversa.
if(enable_true) {
timerWrite(timer_c,0);
timerAlarmEnable(timer_c);
}
if(!enable_true) timerAlarmDisable(timer_c);
antibounce=0; //Variable que recoge si ha pasado el tiempo necesario para evitar rebotes desde la última interrupción.
timerWrite(timer_b,0); //Reinicio del timer que medirá el tiempo desde la última interrupción.
timerAlarmEnable(timer_b); //Puesta en marcha del timer.
}
}
volatile float rising_count=0; //Contador de los flancos de subida de la señal. Entre dos flancos transcurre un periodo.
void IRAM_ATTR ISR_rising() { //Interrupción activada con los flancos de subida de la señal.
rising_count++; //Aumenta el contador a cada ejecución. Al ser una señal digital, no tiene rebotes.
}
volatile float ts=5000; //Tiempo de medida en ms
volatile float timerFC=(ts*1000)/2.88; //Con el PRE configurado a 1200=200+10^3 el fin de cuenta del timer (timerFC) del timer debe ser
//ts en microsegundos (us) dividido entre 2,88 us, que es el tiempo entre cada pulso del PRE para el FC fijado
volatile int flag=0; //Variable que registra los cambios en el tiempo de medida para lanzar un mensaje de aviso.
void IRAM_ATTR ISR_up() { //Aumento del tiempo de medida
if(antibounce) { //Antibounce valdrá 1 si ha pasado el tiempo necesario para evitar rebotes con el pulsador (timer_b).
ts+=1000; //Se aumenta el tiempo de medida.
timerFC=(ts*1000)/2.88; //Se realiza el calculo de la FC que debe introducirse al timer.
timerAlarmDisable(timer); //Se deshabilita el timer antes de modificar su configuración.
timerAlarmWrite(timer,timerFC,true); //Se introduce la nueva FC, aumentando en un segundo el tiempo de medida.
timerWrite(timer,0); //Se reinicia el timer.
timerAlarmEnable(timer); //Se vuelve a activar el timer.
rising_count=0; //Para dar una medida precisa de la frecuencia, se reinicia el contador de flancos para que no quede residuo del tiempo anterior.
flag=1; //Actulización de la variable de aviso.
antibounce=0; //Reinicio de antibounce. Un timer devolverá su valor a 1 pasados tk ms.
timerWrite(timer_b,0); //Configuración del timer_b.
timerAlarmEnable(timer_b);
}
}
void IRAM_ATTR ISR_down() { //Disminución del tiempo de medida. Mismo fundamento que en la anterior interrupción.
if(antibounce) { //Mismo funcionamiento que las ISR anteriores.
ts-=1000;
timerFC=(ts*1000)/2.88;
timerAlarmDisable(timer);
timerAlarmWrite(timer,timerFC,true);
timerWrite(timer,0); //Reiniciar el timer es especialmente importante aquí.
timerAlarmEnable(timer);
rising_count=0;
flag=1;
antibounce=0;
timerWrite(timer_b,0);
timerAlarmEnable(timer_b);
}
}
float frec=11; //Frecuencia de la señal a medir (Hz).
volatile float period=1000/frec;
void setup() {
Serial.begin(115200);
Serial.println("Frecuencímetro con timers");
///////////////////////////////////////// CONFIGURACIÓN DE PINES ////////////////////////////////////////
pinMode(enable, INPUT);
pinMode(up_buton, INPUT);
pinMode(down_buton, INPUT);
/////////////////////////////////////CONFIGURACIÓN DE INTERRUPCIONES ////////////////////////////////////////
attachInterrupt(digitalPinToInterrupt(enable),ISR_enable,FALLING); //Interrupción 0, activa o desactiva la señal.
attachInterrupt(digitalPinToInterrupt(up_buton),ISR_up, FALLING); //Interrupción 1, aumenta el tiempo de medida.
attachInterrupt(digitalPinToInterrupt(down_buton),ISR_down,FALLING); //Interrupción 2, disminuye el tiempo de medida:
attachInterrupt(digitalPinToInterrupt(signal_in),ISR_rising,RISING); //Contador de flancos de la señal
pinMode(signal_in, INPUT);
pinMode(signal_out, OUTPUT);
////////////////////////////////////////// INICIALIZACIÓN DE LOS TIMERS ///////////////////////////////////////
timer=timerBegin(0,230,true);
timerAttachInterrupt(timer,&timer_end,true);
timerAlarmWrite(timer,(5e6/2.88),true);
timerAlarmEnable(timer);
timer_b=timerBegin(1,230,true);
timerAttachInterrupt(timer_b,&timer_b_end,true);
timerAlarmWrite(timer_b,(tk*1000/2.88),false);
timerAlarmEnable(timer_b);
timer_c=timerBegin(2,230,true);
timerAttachInterrupt(timer_c,&timer_c_end,true);
timerAlarmWrite(timer_c,period*500/2.88,true);
}
float frecuency; //Frecuencia calculada
float interval; //Intervalo de medida calculado
int first; //Primer valor del puerto serie.
int figures=0; //Contador del número de datos introducidos en el puerto serie (cifras de la frecuencia).
int decimals=1; //Contador de cifras decimales. Vale 1 porque el contador de cifras siempre tiene un valor una cifra superior a la real.
int dec_counter=0; //Habilita las cifras decimales
void loop() {
if(semiperiod) { //Transcurrida la mitad del periodo de la señal, cambia su valor de alto a bajo y viceversa
digitalWrite(signal_out, !digitalRead(signal_out)); //Invierte el output.
semiperiod=0;
}
if(flag) { //Si se modificó el tiempo de subida, el temporizador se habrá reiniciado, lo que ocasiona esperas mayores, ya que un nuevo
//intervalo de medida se iniciará. FLAG vale 1 si se ha modificado el intervalo de medida y lanza un mensaje de aviso.
Serial.println("Tiempo de medida modificado\nReiniciando cálculos...");
flag=0; //Reinicio de FLAG
}
if(count_finish) { //CONUNT_FINISH vale 1 cuando el timer finaliza.
interval=(timerFC*2.88)/1e6; //Calculo del intervalo de medida a través de timerFC (en segundos).
frecuency=rising_count/interval; //Calculo de la frecuencia a partir del numero de periodos, contabilizado por medio de los flancos de
//subida, dividido entre el intervalo de medida (HZ).
rising_count=0; //Reinicio de contadores
count_finish=0;
Serial.printf("Frecuencia= %f\n", frecuency); //Resultados de los cálculos.
Serial.printf("Frecuencia real: %f\n", frec);
Serial.printf("Medida en intervalos de: %f s\n\n", interval);
}
//Configuración del puerto de serie//
if(Serial.available()) { //Detecta si hay bits en el puerto serie.
first=Serial.read(); //Guarda el primer valor del puerto serie en ASCII y lo elimina del puerto serie.
if(first>47&&first<58){ //Se ejecuta solo si el valor leido es un número.
if(!figures) frec=0; //Borra el dato de frecuencia para hacer los cálculos
first=first-48; //Convierte el código ASCII a número decimal.
frec=frec+first*pow(10,-figures); //Suma el valor del número a la frecuencia según su peso (El primer número sumado tiene un peso de 1, el
//peso de los sucesivos números introducidos decrece 10 veces respecto al anterior. Así, introcudir en el
//puerto serie el número 234,5 resultará en frec=2*10^0+3*10^-1+4*10^-2=2,345).
figures++; //Cuenta cuantas cifras tiene la frecuencia introducida.
if(dec_counter) decimals++; //Si dec_counter es 1, contará las cifras decimales.
}
if(first==10){ //Lee el ENTER con el que termina la cadena leida con el puerto serie. Esta es la orden para modificar los timers.
frec=frec*pow(10,figures-decimals); //Multiplica frec por 10 elevado a (N de cifras-N de cifras decimales) para convertirla en el número original.
//(2,345*10^(cifras-decimales) = 234,5)
figures=0; //Reinicia el contador de cifras.
dec_counter=0; //Reinicia la variable que habilita el conteo de decimales.
decimals=1; //Reinicia el contador de decimales.
period=1000/frec; //Calcula el periodo de la señal.
timerAlarmDisable(timer_c); //Detiene el timer que genera la señal.
timerAlarmWrite(timer_c,period*500/2.88,true); //Introduce en el timer el dato de FC correspondiente a la nueva frecuencia.
timerWrite(timer_c,0); //Reinicia el timer de la señal.
timerAlarmEnable(timer_c); //Activa el timer de la señal.
timerWrite(timer,0); //Reinicia el timer de medida.
rising_count=0; //Reinicia el contador de flancos.
Serial.printf("Frecuencia fijada a: %f\n", frec); //Mensaje de advertencia.
}
if(first==44) dec_counter=1; //Si lee una coma, activará a partir de aquí el conteo de decimales.
}
}