const int senyal_output = 23; // pin generacion señal auto-generada
const int senyal_entrada = 18; // pin interrupcion para la entrada de señal
const int gen_senyal=35; // pin interrupción que habilita la señal auto-generada
const int sube = 32; // pin interrucpción que aumenta 1 seg el tiempo de medida, hasta 9 seg.
const int baja = 33; // pin interrucpción que disminuye 1 seg el tiempo de medida, hasta 1 seg.
int senyal=0; //flag para indicar cuando cambia auto-generada la señal de alto a bajo
int medida=0; //glag para indicar indica cuando se acaba la medición de la señal
float frec_senyal = 5.8; //frecuencia inicial de la señal auto-generada en HZ
volatile unsigned long current_millis = 0;// control rebotes
volatile unsigned long last_interrupt_time = 0;
volatile int nflancos=0; //flancos contados de la señal
volatile float t_medida=5; //tiempo de medición inicial en segundos
float frecuencia; //frecuencia medida a mostrar
hw_timer_t * timer0 = NULL; //estructura de datos del timer0
hw_timer_t * timer1 = NULL; //estructura de datos del timer1
#define debounce_time 200 //tiempo mínimo entre pulsaciones
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR ISR_gen_senyal(){ //cuando se pulse el botón amarillo entrará, y se habilitarán las alrarmas de los timers
current_millis = millis();
if (current_millis - last_interrupt_time > debounce_time) //control de rebotes, para que solo se ejecute una vez cuando pulsemos el botón
{
timerWrite(timer0, 0); //se resetean a 0 los timers
timerWrite(timer1, 0);
timerAlarmEnable(timer0);// se activan las alarmas de los timers
timerAlarmEnable(timer1);
last_interrupt_time = current_millis;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR ISR_sube(){ //al pulsar el botón para aumentar el tiempo de medida de la frecuencia se suma un segundo al tiempo anterior
current_millis = millis();
if (current_millis - last_interrupt_time > debounce_time) //control de rebotes, para que solo se ejecute una vez cuando pulsemos el botón
{
timerAlarmDisable(timer0);//se deshabilitan las alarmas de los timers, para no tener problemas
timerAlarmDisable(timer1);
nflancos=0; //se pone a cero el numero de flancos, porque se va a iniciar una medición nueva
t_medida++; //se aumenta una unidad el tiempo de medida, en este caso un segundo
if(t_medida>9){ // como no debemos pasar de 9 segundos, al detectarlo le asignamos un valor máximo de 9
t_medida=9;
}
timerAlarmWrite(timer1, t_medida/(280/80e6), true); //se configura la alarma del timer1 con el nuevo valor de tics
timerWrite(timer0, 0); //se pone a 0 el valor de cuenta del timer0 y timer1
timerWrite(timer1, 0);
timerAlarmEnable(timer0);// se activan las alarmas de nuevo
timerAlarmEnable(timer1);
last_interrupt_time = current_millis; //se actualiza el tiempo de la última interrupción
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR ISR_baja(){ //al pulsar el botón para bajar el tiempo de medida de la frecuencia se baja un segundo al tiempo anterior
current_millis = millis();
if (current_millis - last_interrupt_time > debounce_time) //control de rebotes, para que solo se ejecute una vez cuando pulsemos el botón
{
timerAlarmDisable(timer0); //se deshabilitan las alarmas de los timers, para no tener problemas
timerAlarmDisable(timer1);
nflancos=0; //se pone a cero el numero de flancos, porque se va a iniciar una medición nueva
t_medida--; //se resta una unidad el tiempo de medida, en este caso un segundo
if(t_medida<1){ //como no debemos pasar de 1 segundo, al detectarlo le asignamos un valor mínimo de 1 segundo
t_medida=1;
}
timerAlarmWrite(timer1, t_medida/(280/80e6), true); //se configura la alarma del timer1 con el nuevo valor de tics
timerWrite(timer0, 0); //se pone a 0 el valor de cuenta del timer0 y timer1
timerWrite(timer1, 0);
timerAlarmEnable(timer0); // se activan las alarmas de nuevo
timerAlarmEnable(timer1);
last_interrupt_time = current_millis; //se actualiza el tiempo de la última interrupción
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR ISR_senyal(){ // cuenta los flancos de subida y bajada de la señal de entrada
nflancos++;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR finTimer0() { //cuando salte la alarma del timer0 se cambia el valor de la "flag" senyal a 1, para que cambie de alto a bajo o viceversa la señal
senyal=1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void IRAM_ATTR finTimer1() { //cuando salte la alarma del timer1 se cambia el valor de la "flag" medida a 1 , para mostrar la frecuencia medida en el puerto serie
medida=1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(){
Serial.begin(115200); //velocidad de comunicación con puerto serie
pinMode(senyal_output, OUTPUT); //configuración de pines
pinMode(senyal_entrada, INPUT);
pinMode(gen_senyal, INPUT);
pinMode(sube, INPUT);
pinMode(baja, INPUT);
//interrupciones
attachInterrupt(digitalPinToInterrupt(senyal_entrada), ISR_senyal, CHANGE); //interrupción para contar flancos de bajada y subida de la señal de interés
attachInterrupt(digitalPinToInterrupt(sube), ISR_sube, FALLING); //interrupción para añadir segundos de medición
attachInterrupt(digitalPinToInterrupt(baja), ISR_baja, FALLING); //interrupción para restar segundos de medición
attachInterrupt(digitalPinToInterrupt(gen_senyal), ISR_gen_senyal, FALLING); // interrucpción para habilitar la señal auto-generada
//timers // fclk =80 MHz / prescaler = 280 = 285714 tics / sec
//timer0 es el encargado de gestionar los pulsos de la señal auto_generada, de tal manera que cada vez que cuenta la mitad del periodo se activa la alarma para cambiar el nivel de la señal
timer0 = timerBegin(0, 280, true); //se inicializa el timer0 ascedente
timerAttachInterrupt(timer0, &finTimer0, true); // se asocia la interrupción al timer0 por flanco
timerAlarmWrite(timer0, ((1/frec_senyal)/2)/(280/80e6), true); //salta la alarma cada medio periodo y tiene AUTORELOAD
//timer1 es el encargado de controlar el tiempo de medida de la frecuencia, que es modificado pulsando los botones asociados
timer1 = timerBegin(1, 280, true); //se inicializa el timer1 ascedente
timerAttachInterrupt(timer1, &finTimer1, true); //se asocia la interrupción al timer1 por flanco
timerAlarmWrite(timer1, t_medida/(280/80e6), true); //salta la alarma del timer1 cuando se alcance el tiempo t_medida, y tiene AUTORELOAD
Serial.println("\n\nFrecuencímetro con Timers\n");
Serial.println("Pulse el botón amarillo para auto generar la señal\n");
Serial.println("Para cambiar la frecuencia, escribe el nuevo valor\n\n");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
delay(1); // solo para el simulador, para mejorar rendimiento
if(senyal==1){ //se genera un pulso de la señal HIGH o LOW dependiendo del valor anterior
digitalWrite(senyal_output, !digitalRead(senyal_output) );
senyal=0; //se devuelve la "flag" a 0 para que no vuelva a entrar hasta que salte de nuevo al alarma del timer0
}
if(medida==1){ //se muestra la fecuencia medida pasados el tiempo de medición deseado
timerAlarmDisable(timer0); //se desactivan las alarmas para evitar problemas
timerAlarmDisable(timer1);
medida=0; //se devuelve la "flag" a 0 para que no vuelva a entrar hasta que salte de nuevo al alarma del timer1
frecuencia=((float)nflancos/2)/t_medida; // como cada ciclo tiene dos flancos, uno de subida y otro de bajada. La mitad de flancos contados entre el tiempo de medida es la frecuencia medida por nuestro frecuencímetro
Serial.printf("Frec: %4.4f Hz - error %4.4f Hz - tiempo: %1.f seg\n", frecuencia,frecuencia-frec_senyal,t_medida);
nflancos=0; //se resetea el numero de flancos para que no se acumulen
timerWrite(timer0, 0); //se devuelven a 0 los timers
timerWrite(timer1, 0);
timerAlarmEnable(timer0); // se activan las alarmas de nuevo
timerAlarmEnable(timer1);
}
if (Serial.available() > 0) { //modifica el valor de la frecuecia autogenerada a través del puerto serie
timerAlarmDisable(timer0); //se desactivan las alarmas para evitar problemas
timerAlarmDisable(timer1);
frec_senyal = Serial.readString().toFloat(); //lee la cadena de caracteres del puerto serie y lo convierte a coma flotante
timerAlarmWrite(timer0, ((1/frec_senyal)/2)/(280/80e6), true); //se configura la alarma del timer0 con el nuevo valor de frecuencia
timerWrite(timer0, 0); //se devuelven a 0 los timers
timerWrite(timer1, 0);
timerAlarmEnable(timer0); // se activan las alarmas de nuevo
timerAlarmEnable(timer1);
nflancos=0; //se resetea el numero de flancos para la nueva medición
Serial.printf("\nMidiendo frecuencia de %4.4f Hz en %1.f seg\n",frec_senyal,t_medida);
}
}