Necesito medir la frecuencia de la onda cuadrada que puede variar entre 0 y 1MHz, y tiene una resolución de 0,25Hz.
Todavía no he decidido qué controlador, pero lo más probable es que sea uno de los Attiny de 20 pines.
Normalmente, la forma en que mediría las señales de baja frecuencia sería utilizando dos temporizadores, uno configurado en modo de captura de temporizador para interrumpir, por ejemplo, en los bordes ascendentes de la señal externa y otro temporizador configurado para interrumpir cada segundo, por lo que el valor del registro del contador de los primeros temporizadores después de 1 segundo sería igual a la frecuencia de la señal.
Sin embargo, este método obviamente no funcionará para capturar señales que oscilen entre 0 y 1MHz con una resolución de 0.25Hz para esto necesitaría un contador de 22Bit (AFAIK los micros de 8bit solo tienen contadores de 8/16bit).
Una idea que tenía era dividir la señal antes de aplicarla al micro, pero esto sería impracticable, ya que la señal tendría que ser dividida por 61, por lo que la frecuencia sólo podría actualizarse cada 61 segundos, cuando me gustaría que fuera cada pocos segundos.
¿Existe otro método que permita actualizar la frecuencia, por ejemplo, cada 4 segundos?
Actualización:
La solución más sencilla es utilizar una interrupción externa o una captura del temporizador para interrumpir en el flanco de subida de la señal y tener el isr
incrementar una variable de tipo long int
. Lea la variable cada 4 segundos (para permitir que se midan frecuencias de hasta 0,25Hz).
Actualización 2:
Como ha señalado JustJeff, una MCU de 8 bits no podrá seguir el ritmo de una señal de 1MHz, por lo que se descarta la interrupción en cada flanco ascendente y el incremento de un long int
...
He elegido el método sugerido por timororr. Una vez que lo ponga en práctica, enviaré un mensaje y compartiré los resultados. Gracias a todos por sus sugerencias.
Informe de progreso:
He empezado a probar algunas de las ideas presentadas aquí. En primer lugar he probado el código de vicatcu. Hubo un problema obvio de TCNT1 no se borra después de la frecuencia se calcula - no es un gran problema ...
Luego me di cuenta al depurar el código que aproximadamente cada 2 a 7 veces que se calculaba la frecuencia la cuenta de desbordamiento del temporizador 1 (el temporizador configurado para contar los eventos externos) se quedaba corta por dos. Lo achaqué a la latencia del ISR del temporizador 0 y decidí trasladar el bloque de la sentencia if del ISR al main (ver el fragmento de abajo) y simplemente poner una bandera en el ISR. Un poco de depuración mostró que la primera medición estaría bien pero con cada lectura subsiguiente la cuenta de desbordamiento del Timer 1 se sobrepasaría en 2. Lo cual no puedo explicar - yo hubiera esperado que estuviera por debajo y no por encima...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
A continuación decidí que intentaría poner en práctica la sugerencia de timrorrs. Para generar el intervalo necesario (de aproximadamente 15ms entre cada interrupción timer_isr) tendría que conectar en cascada los dos temporizadores de 8 bits ya que el único temporizador de 16 bits del Atmega16 está siendo utilizado para capturar los flancos de subida de la señal externa.
Pensé que esta solución funcionaría y sería mucho más eficiente ya que la mayor parte de la sobrecarga se desplaza a los temporizadores y sólo queda un isr corto para que la cpu lo maneje. Sin embargo, no fue tan preciso como esperaba, las mediciones se desplazaron hacia adelante y hacia atrás por aproximadamente 70Hz que no me importa en las frecuencias altas, pero definitivamente no es aceptable en las frecuencias más bajas. No pasé mucho tiempo analizando el problema, pero supongo que la disposición en cascada del temporizador no es tan precisa ya que he implementado un arreglo similar a la sugerencia de timrorrs en un controlador 8051 mucho más lento que tenía 2 temporizadores de 16 bits y los resultados fueron bastante precisos.
Ahora he vuelto a la sugerencia de vicatcu, pero he trasladado el cálculo de la frecuencia a la isr del temporizador 0 (véase el fragmento siguiente ), este código ha producido mediciones consistentes y razonablemente precisas. Con un poco de calibración, la precisión debería ser de aproximadamente +/-10Hz.
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER0_PRELOAD; //Reload timer for 1KHz overflow rate
if(task_timer_ms > 0)
{
task_timer_ms--;
}
else
{
frequency_hz = 1.0 * TCNT1;
TCNT1 = 0;
frequency_hz += global_num_overflows * 65536.0;
global_num_overflows = 0;
frequency_hz /= (TASK_PERIOD_MS / 1000.0);
task_timer_ms = TASK_PERIOD_MS;
}
}
Si alguien tiene alguna otra sugerencia estoy abierto a ellas aunque prefiero no tener que usar rangos... Además, ya no quiero conseguir una resolución del 0,25%, no parece que tenga mucho sentido con el nivel de precisión que tengo en este momento.