//ejercicio frecuentimetro
const int senyal_output = 23; // pin generacion señal simulación
const int senyal_entrada = 18; // GPIO pin interrupcion para la entrada de señal
const int sube = 27; // aumenta 1 seg el tiempo de medida, hasta 9 seg.
const int baja = 25; // disminuye 1 seg el tiempo de medida, hasta 1 seg.
const int pulsador_inicio = 33; //su funcion es iniciar el programa
volatile float frec_senyal; //variable de la frecuencia
volatile float periodo_senyal_ms = 1000 * (1/frec_senyal); // periodo en ms
// tiempo_us establece cada cuanto tiempo entra en la interrupcion del timers
volatile float tiempo_us=1e6/(230/80);
volatile int cuenta; // variable se incrementa en la ISR y se decrementa en main
// contador numero de veces se ejecutan la ISR
volatile int nflancos=0;
volatile int inicio = 0;
volatile int nveces_ISR=0;
volatile unsigned long current_millis = 0;//toma valores de tiempo
volatile unsigned long last_interrupt_time = 0; // inicialmente 0
#define espera 200 // 200 ms es un valor muy conservador
hw_timer_t * timer = NULL;// estructura de datos del timer
//ISR de la senyal de entrada
void IRAM_ATTR ISR_senyal()
{
//la variable incrementa de valor en los flancos
nflancos++;
}
//ISR del pulsador de inicio
void IRAM_ATTR ISR_inicio()
{
inicio++;
}
//ISRs de los pulsadores para aumentar y disminuir el tiempo de medida
void IRAM_ATTR ISR_sube()
{
nveces_ISR++; // acumula el numero de veces que se entra en la ISR
current_millis = millis(); //establece en la variable el tiempo del programa
// Si la IRQ viene antes de “debounce_time” ms, se asume que es un "rebote"
if (current_millis - last_interrupt_time > espera)
{
// el tiempo de medida va de 1segundo a 1segundos aumentando 1 segundo
// si el tiempo es mayor a 9 segundos se reinicia a 1segundo
if(tiempo_us >= 9e6/(230/80)) tiempo_us = 1e6/(230/80);
else tiempo_us += 1e6/(230/80);
last_interrupt_time = current_millis;//almacena en otra variable el nuevo tiempo
}
}
void IRAM_ATTR ISR_baja()
{
nveces_ISR++; // acumula el numero de veces que se entra en la ISR
current_millis = millis(); //establece en la variable el tiempo del programa
// Si la IRQ viene antes de “debounce_time” ms, se asume que es un "rebote"
if (current_millis - last_interrupt_time > espera)
{
// el tiempo de medida va de 9segundos a 1segundo disminuyendo 1 segundo
// si el tiempo es menor de 1 segundo se reinicia a 9segundos
if(tiempo_us <= 1e6/(230/80)) tiempo_us = 9e6/(230/80);
else tiempo_us -= 1e6/(230/80);
last_interrupt_time = current_millis;//almacena en otra variable el nuevo tiempo
}
}
//ISR del timer
void IRAM_ATTR finTimer()
{
cuenta=1;
}
// falta completar la funcion de setup()
void setup()
{
//configuramos todos los pines de entrada/salidas
Serial.begin(9600);
pinMode(senyal_output, OUTPUT);
pinMode(senyal_entrada, INPUT);
pinMode(sube, INPUT);
pinMode(baja, INPUT);
pinMode(pulsador_inicio, INPUT);
//habilitamos las ISRs
attachInterrupt(digitalPinToInterrupt(senyal_entrada), ISR_senyal,RISING);
attachInterrupt(digitalPinToInterrupt(baja), ISR_baja,FALLING);
attachInterrupt(digitalPinToInterrupt(sube), ISR_sube,FALLING);
attachInterrupt(digitalPinToInterrupt(pulsador_inicio),ISR_inicio,FALLING);
/*establecemos el preescaler en 230, lo que reduce la frecuencia de la señal 230 veces, por lo que
el periodo nuevo es igual a (80/230)1e-6 segundos*/
timer = timerBegin(0, 230, true);
//creamos la llamada a la interrupcion (finTimer) en flanco(true)
timerAttachInterrupt(timer, &finTimer, true);
//decimos cada cuanto se dispone a entrar en la interrupción, entra cada tiempo_us.
//ademas tiene un autoreload(true) por lo que se ejecuta constantemente
timerAlarmWrite(timer, tiempo_us , true);
//habilitamos el temporizador
timerAlarmEnable(timer);
}
//con estas dos variablesvamos tomando valores de tiempo dentro de la sentencia para generar la señal
unsigned long tp1=0; // control de la señal frecuencia
unsigned long tp2=0;
unsigned long current_time;
void loop()
{
//si se pulsa el pulsador de inicio ejecuta el codigo
if(inicio!=0)
{
delay(1);//solo en el simulador, para q no tenga que procesar tantos "if", y vaya más rápido
current_time=millis();//esta variable toma el valor del tiempo en el que inicia el loop
//sentencia para generar la senyal con una determinada frecuencia
/* al haber pasado la frecuenca a periodo (tiempo), creamos un if, para que
durante la mitad de ese periodo, el pin de salida (senyal_output), genere un valor
de high, y la otra mitad del periodo, genere un valor de low */
if( current_time-tp1 > periodo_senyal_ms/2)
{
digitalWrite(senyal_output, !digitalRead(senyal_output) );
tp1=millis(); // tp1 toma el valor del tiempo
}
/* en este segundo if lo que hacemos es que cuando el tiempo transcurrido, desde el inicio
del loop, hasta un determinado tiempo (tp2), sea mayor a la medida de tiempo que deseamos,
medimos la frecuencia de la señal obtenida por el puerto GPIO18,
que es el resultado de calcular el numero de flancos de subida entre el tiempo de medida
este resultado no es exacto pero cada vez que vamos aumentando el tiempo de medida
el resultado es cada vez más acertado.*/
if(current_time-tp2>tiempo_us*2/1000)
{
float frec_medida=nflancos/(tiempo_us*2/1e6);
Serial.printf("Frecuencia señal de entrada medida:%f, ",frec_medida);
Serial.printf("flancos contandos. %d en %fs\n",nflancos,tiempo_us*2/1e6);
nflancos=0;
tp2=millis();// tp2 toma el valor del tiempo
}
}
//con esta sentencia habilitamos leer cualquier valor desde el puerto serie
//y establecer ese valor como la frecuencia de la señal que queremos crear
if (Serial.available() > 0)
{
frec_senyal = Serial.readString().toFloat();//función para leer cualquier valor
Serial.printf("Su frecuencia nueva es: %fHz\n",frec_senyal);//muestra por pantalla
periodo_senyal_ms = 1000 * (1/frec_senyal);//convertimos la frecuencia a periodo
}
}