Preciso medir a frequência da onda quadrada que pode variar entre 0 e 1 MHz e tem uma resolução de 0,25 Hz.
Ainda não decidi em qual controlador, mas provavelmente será um dos 20 pinos da Attiny.
Normalmente, como eu mediria os sinais de frequência mais baixa seria usando dois temporizadores, um configurado no modo de captura do temporizador, para interromper, por exemplo, as bordas ascendentes do sinal externo e outro temporizador configurado para interromper a cada segundo, portanto, os temporizadores anteriores registram o valor após 1 segundo seria igual à frequência do sinal.
No entanto, este método obviamente não funcionará para capturar sinais que variam entre 0 e 1 MHz com uma resolução de 0,25 Hz para isso. Eu precisaria de um contador de 22 bits (os micros AFAIK de 8 bits possuem apenas contadores de 8/16 bits).
Uma idéia que tive foi dividir o sinal antes de aplicá-lo ao micro, mas isso seria impraticável, pois o sinal teria que ser dividido por 61. Portanto, a frequência só poderia ser atualizada a cada 61 segundos, onde eu gostaria que fosse a cada poucos segundos .
Existe outro método que permita a atualização da frequência, digamos a cada 4 segundos?
Atualizar:
A solução mais simples é usar uma interrupção externa ou uma captura de temporizador para interromper a borda ascendente do sinal e fazer com que o isr
incremento seja uma variável do tipo long int
. Leia a variável a cada 4 segundos (para permitir frequências de até 0,25Hz a serem medidas).
Atualização 2:
Como apontado por JustJeff, um MCU de 8 bits não será capaz de acompanhar um sinal de 1 MHz, de modo que exclui a interrupção em cada borda ascendente e o incremento de um long int
...
Eu escolhi o método sugerido por timororr. Depois que eu começar a implementá-lo, vou postar de volta e compartilhar os resultados. Obrigado a todos por suas sugestões.
Relatório de progresso:
Iv'e começou a testar algumas das idéias apresentadas aqui. Primeiramente, tentei o código do vicatcu. Havia um problema óbvio de TCNT1 que não foi resolvido depois que a frequência foi calculada - não é grande coisa ...
Percebi então, ao depurar o código, que a cada 2 a 7 vezes a frequência era calculada no temporizador 1 (o temporizador configurado para contar eventos externos) a contagem de transbordamento seria curta em dois. Coloquei isso na latência do Timer 0 ISR e decidi mover o bloco if do ISR para o principal (veja o trecho abaixo) e apenas defina uma sinalização no ISR. Alguma depuração mostrou que a primeira medição seria boa, mas a cada leitura subseqüente a contagem de transbordamento do Timer 1 terminaria em 2. o que não posso explicar - eu esperava que ela estivesse abaixo do limite ...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Em seguida, decidi que tentaria implementar a sugestão de timrorrs. Para gerar o intervalo necessário (de aproximadamente 15ms entre cada interrupção timer_isr), eu precisaria cascatear os dois temporizadores de 8 bits, pois o único temporizador de 16 bits do Atmega16 está sendo utilizado para capturar as bordas ascendentes do sinal externo.
Eu pensei que essa solução funcionaria e seria muito mais eficiente, pois a maior parte da sobrecarga é deslocada para os temporizadores e apenas um isr curto resta para a CPU lidar. No entanto, não foi tão preciso quanto eu esperava, as medições mudaram para frente e para trás em aproximadamente 70Hz, o que eu não me importaria em altas frequências, mas definitivamente não é aceitável em frequências mais baixas. Eu não gastei muito tempo analisando o problema, mas acho que o arranjo em cascata do temporizador não é tão preciso, já que implementei um arranjo semelhante à sugestão de timrorrs em um controlador 8051 muito mais lento que tinha 2 temporizadores de 16 bits e os resultados foram bastante precisos.
Voltei agora à sugestão do vicatcu, mas mudei o cálculo de frequência para o temporizador 0 isr (veja o trecho abaixo ), esse código produziu medições consistentes e razoavelmente precisas. Com um pouco de calibração, a precisão deve ser 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;
}
}
Se alguém tiver outras sugestões, estou aberto a eles, embora eu prefira não usar intervalos ... Também não tenho mais a intenção de obter uma resolução de 0,25%, não parece haver muito sentido no nível de precisão que tenho no momento. .