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. .
Respostas:
Se possível, sugiro selecionar um microcontrolador que suporte uma operação de contador usando as entradas do timer; em vez de incrementar manualmente um contador dentro de um ISR (que em altas frequências acaba rapidamente saturando a atividade do microcontrolador), você permite que o hardware lide com a contagem. Nesse ponto, seu código simplesmente se torna uma questão de aguardar sua interrupção periódica e calcular a frequência.
Para estender a faixa e tornar o contador de frequência mais generalizado (removendo a necessidade de várias faixas à custa de um pouco mais de trabalho para o MCU), você pode usar a seguinte técnica.
Selecione uma taxa de interrupção periódica que permita a precisão da medição na frequência de entrada mais alta; isso deve levar em consideração o tamanho do seu contador (é necessário selecionar o período do temporizador para que o contador não transborde na freqüência máxima de entrada). Neste exemplo, assumirei que o valor do contador de entrada pode ser lido da variável "timer_input_ctr".
Inclua uma variável para contar interrupções periódicas (deve ser inicializada como 0 na inicialização); neste exemplo, vou me referir a essa variável como "isr_count". O período de interrupção está contido na constante "isr_period".
Sua interrupção periódica deve ser implementada como (pseudocódigo C):
Obviamente, este exemplo aproxima-se de algumas matemáticas de ponto flutuante que podem não ser compatíveis com microcontroladores de gama baixa, existem técnicas para superar isso, mas estão fora do escopo desta resposta.
fonte
Você pode considerar ter dois (ou mais) intervalos. Os problemas com a captura de frequências muito baixas são um pouco diferentes dos problemas com as mais altas. Como você já observou, na extremidade superior do seu intervalo, você tem problemas com o estouro de contador.
Mas considere que na extremidade inferior do seu intervalo, sua precisão sofrerá por não ter contagens suficientes no registro. Não tenho certeza se você realmente deseja discriminar entre 0,25Hz e 0,5Hz, mas se o fizer, precisará contar quatro segundos para fazer isso.
Além disso, especificar uma resolução plana de 0,25Hz, estritamente interpretada, significa que você poderá discernir 500.000,00Hz de 500.000,25Hz, o que é um alto grau de precisão.
Por esses motivos, o design de intervalos distintos pode aliviar o problema do tamanho do contador. Puxar números aleatoriamente, por exemplo, para os mais baixos, digamos de 0 a 100Hz, conte com intervalos de 10 segundos e você obtém uma resolução de 0,1Hz, e o contador precisa apenas subir até 1000, nem mesmo 10 bits. Em seguida, de 100Hz a 10kHz, conte por intervalos de 1 segundo; você obtém apenas uma resolução de 1 Hz, mas seu contador precisa executar até 10.000 ainda menores que 16 bits. O intervalo superior de 10kHz a 1MHz pode contar por apenas 0,01 s, e a contagem máxima ainda seria de apenas 10.000 e, embora a sua resolução fosse 100Hz, seria uma precisão razoável.
fonte
Você pode misturar um contador de hardware e software contando os estouros do contador de hardware em um ISR.
A contagem de cada extremidade do sinal em um ISR será muito lenta para um sinal de 1 MHz. Eu acho que você poderia fazer até 50kHz dessa maneira.
fonte
Em vez de fazer um contador de 1 segundo, faça um contador de 0,1 segundo e multiplique a contagem por 10?
Se for apenas uma questão de armazenar o número do contador, você não pode usar um código adicional para acompanhar quando o contador está prestes a transbordar e gravar em outro local de memória para manter o registro?
fonte
Você não pode simplesmente usar a captura de entrada de um temporizador de 16 bits e as interrupções de transbordamento (mais uma variável) para fazer a medição? Aqui está como eu faria isso com o ATTiny24A com AVR-GCC (não testado e possivelmente com erros, é claro):
... de qualquer forma, compila :)
EDIT Eu olhei para a saída do arquivo lss do meu código, e o código gerado tem muitas instruções para não tropeçar em 1MHz com um relógio de 8MHz ... até o simples incremento de uma linha no TIM1_OVF_vect gera 19 instruções! Portanto, para lidar com eventos de 1 MHz, você definitivamente precisaria otimizar, provavelmente registrar alocar algumas coisas (provavelmente num_overflows e capture_value_ticks), usar o assembler inline (roubar as coisas importantes do arquivo lss) e mover o processamento das interrupções para o principal loop sempre que possível.
fonte
Publicar este código como uma alternativa por sugestão de @ timrorr à minha postagem anterior. Isso é compilado para o ATTiny24A usando o padrão da linguagem c99, mas na verdade não o testei além disso.
Esse é um ótimo uso dos recursos de hardware do Timer1 e libera uma tonelada de ciclos de processamento em comparação com a minha postagem original.
fonte
Usando pré-calibradores, é possível obter até a medição em GHz. Este é um medidor de frequência simples de 40 MHz com ATMEL AVR AT90S2313: http://www.myplace.nu/avr/countermeasures/index.htm
Aqui estão alguns outros projetos semelhantes:
http://www.ikalogic.com/freq_meter_2.php
http://www.saturn.dti.ne.jp/~khr3887/lfcd_e.html
http://www.circuitlake.com/rs232-frequency-meter-and-pulse-generator.html
http://www.ulrichradig.de/home/index.php/avr/frequenzcounter
http://www.triplespark.net/elec/analysis/FreqCnt/
http://www.cappels.org/dproj/30MHzfmeter/30MhzFmtr.html
http://www.qsl.net/pa3ckr/bascom%20and%20avr/rfcounter/index.html
http://www.sump.org/projects/counter
http://digilander.libero.it/alfred73/eprojects.htm#1300%20Mhz%20Frequencymeter%20with%20prescaler
fonte