Necesito medir la frecuencia de onda cuadrada que puede variar entre 0 y 1MHz, y tiene una resolución de 0.25Hz.
Aún no he decidido qué controlador, pero probablemente será uno de los 20pin Attiny's.
Normalmente, la forma en que mediría las señales de frecuencia más baja sería mediante el uso de dos temporizadores, uno configurado en el modo de captura con temporizador para interrumpir, digamos los flancos ascendentes de la señal externa y otro programado para interrumpir cada segundo, por lo tanto, los contadores anteriores registran 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 oscilan entre 0 y 1MHz con una resolución de 0.25Hz para esto. Necesitaría un contador de 22 bits (los micros AFAIK de 8 bits solo tienen contadores de 8/16 bits).
Una de las ideas que tuve fue dividir la señal antes de aplicarla al micro, pero esto sería impráctico, ya que la señal tendría que dividirse entre 61, por lo que la frecuencia solo podría actualizarse cada 61 segundos, donde me gustaría. cada pocos segundos.
¿Hay otro método que permita actualizar la frecuencia cada 4 segundos?
Actualizar:
La solución más sencilla es utilizar un uso de una interrupción externa o una captura con temporizador para interrumpir en el flanco ascendente de la señal y hacer que isr
incremente una variable de tipo long int
. Lea la variable cada 4 segundos (para permitir frecuencias de hasta 0.25Hz a las medidas).
Actualización 2:
Como señala JustJeff, una MCU de 8 bits no podrá mantenerse al día con una señal de 1 MHz, por lo que se descarta la interrupción en cada flanco ascendente y se incrementa un long int
...
He elegido el método sugerido por timororr. Una vez que llegue a implementarlo, volveré a publicar y compartiré los resultados. Gracias a todos por vuestras sugerencias.
Informe de progreso:
Iv'e comenzó a probar algunas de las ideas presentadas aquí. En primer lugar probé el código de vicatcu. Hubo un problema obvio de que TCNT1 no se solucionó después de calcular la frecuencia, no es un gran problema ...
Luego, al depurar el código, noté que aproximadamente cada 2 a 7 veces la frecuencia se calculaba el contador de desbordamiento del temporizador 1 (el temporizador configurado para contar eventos externos) se reduciría en dos. Puse esto a la latencia del temporizador 0 ISR y decidí mover el bloque if del bloque ISR al principal (vea el fragmento a continuación) y solo establezca una marca en el ISR. Algunos errores en la depuración mostraron que la primera medición estaría bien, pero con cada lectura posterior, el conteo de desbordamiento del Temporizador 1 terminaría en 2. lo cual no puedo explicar: hubiera esperado que no estuviera por debajo de ...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Luego decidí que intentaría implementar la sugerencia de timrorrs. Para generar el intervalo necesario (de aproximadamente 15 ms entre cada interrupción timer_isr) tendría que conectar en cascada los dos temporizadores de 8 bits. como el único temporizador de 16 bits en el Atmega16 se está utilizando para capturar los flancos ascendentes 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 los gastos generales se desplazan a los temporizadores y solo queda un corto isr para que la CPU la maneje. Sin embargo, no fue tan preciso como esperaba, las mediciones se movieron de un lado a otro en aproximadamente 70Hz, lo cual no me importaría en las frecuencias altas, pero definitivamente no es aceptable en las frecuencias más bajas. No dediqué mucho tiempo a analizar el problema, pero supongo que la disposición en cascada del temporizador no es tan precisa ya que he implementado una disposición 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 moví el cálculo de frecuencia al temporizador 0 isr (vea el fragmento a continuación ), este código ha producido mediciones consistentes y razonablemente precisas. Con un poco de precisión, la precisión debería ser aproximadamente de +/- 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 ellos, pero prefiero no usar rangos ... Tampoco tengo la intención de obtener una resolución del 0,25%, no parece tener mucho sentido con el nivel de precisión que tengo en este momento.