J'ai besoin de mesurer la fréquence de l'onde carrée qui peut varier entre 0 et 1 MHz, et a une résolution de 0,25 Hz.
Je n'ai pas encore décidé sur quel contrôleur mais ce sera probablement l'un des 20pin Attiny.
Normalement, la façon dont je mesurerais les signaux de fréquence inférieure serait d'utiliser deux temporisateurs, l'un configuré en mode de capture de temporisation pour interrompre, disons, les fronts montants du signal externe et un autre temporisateur configuré pour interrompre toutes les secondes, donc l'ancienne valeur de registre du compteur de temporisateurs après 1 seconde serait égal à la fréquence du signal.
Cependant, cette méthode ne fonctionnera évidemment pas pour capturer des signaux compris entre 0 et 1 MHz avec une résolution de 0,25 Hz. Pour cela, j'aurais besoin d'un compteur 22 bits (les micros AFAIK 8 bits n'ont que des compteurs 8/16 bits).
Une idée que j'avais était de diviser le signal avant de l'appliquer au micro mais cela ne serait pas pratique car le signal devrait être divisé par 61 donc la fréquence ne pourrait être mise à jour que toutes les 61 secondes où je voudrais qu'il soit toutes les quelques secondes .
Existe-t-il une autre méthode qui permettrait de mettre à jour la fréquence, disons toutes les 4 secondes?
Mise à jour:
La solution la plus simple consiste à utiliser une interruption externe ou une capture temporisée pour interrompre sur le front montant du signal et faire de l' isr
incrément une variable de type long int
. Lisez la variable toutes les 4 secondes (pour permettre des fréquences jusqu'à 0,25 Hz à mesurer).
Mise à jour 2:
Comme l'a souligné JustJeff, un microcontrôleur 8 bits ne pourra pas suivre un signal de 1 MHz, ce qui exclut l'interruption à chaque front montant et l'incrémentation d'un long int
...
J'ai choisi la méthode proposée par timororr. Une fois que j'arriverai à le mettre en œuvre, je posterai et partagerai les résultats. Merci pour toutes vos suggestions.
Rapport d'étape:
Iv'e a commencé à tester certaines des idées présentées ici. J'ai d'abord essayé le code de vicatcu. Il y avait un problème évident de TCNT1 non résolu après que la fréquence ait été calculée - pas un gros problème ...
Ensuite, j'ai remarqué lors du débogage du code qu'environ toutes les 2 à 7 fois la fréquence était calculée, le nombre de dépassements du temporisateur 1 (le temporisateur configuré pour compter les événements externes) serait court de deux. J'ai mis cela à la latence du minuteur 0 ISR et j'ai décidé de déplacer le bloc d'instructions if du ISR vers le principal (voir l'extrait ci-dessous) et de simplement définir un indicateur dans l'ISR. Un débogage a montré que la première mesure serait correcte, mais à chaque lecture suivante, le nombre de débordements du temporisateur 1 serait dépassé de 2, ce que je ne peux pas expliquer - je m'attendais à ce qu'il ne soit pas inférieur à ...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Ensuite, j'ai décidé d'essayer de mettre en œuvre la suggestion de timrorrs. Pour générer l'intervalle nécessaire (d'environ 15 ms entre chaque interruption timer_isr), je devrais mettre en cascade les deux temporisateurs 8 bits car le seul temporisateur 16 bits de l'Atmega16 est utilisé pour capturer les fronts montants du signal externe.
Je pensais que cette solution fonctionnerait et serait beaucoup plus efficace car la plupart des frais généraux sont décalés vers les temporisateurs et un seul isr court est laissé pour le processeur à gérer. Cependant, ce n'était pas aussi précis que je l'espérais, les mesures se déplaçaient d'avant en arrière d'environ 70 Hz, ce qui ne me dérangerait pas aux hautes fréquences, mais ce n'est certainement pas acceptable aux basses fréquences. Je n'ai pas passé beaucoup de temps à analyser le problème, mais je suppose que l'arrangement en cascade de la minuterie n'est pas aussi précis que j'ai mis en place une disposition similaire à la suggestion de timrorrs sur un contrôleur 8051 beaucoup plus lent qui avait 2 temporisateurs 16 bits et les résultats étaient assez précis.
Je suis maintenant revenu à la suggestion de vicatcu, mais j'ai déplacé le calcul de fréquence dans le temporisateur 0 isr (voir l'extrait ci-dessous ), ce code a produit des mesures cohérentes et raisonnablement précises. Avec un peu de précision de calibrage, il devrait être d'environ +/- 10 Hz.
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 quelqu'un a d'autres suggestions, je suis ouvert à eux, mais je n'ai pas à utiliser de plages ... Je n'ai plus non plus l'intention d'obtenir une résolution de 0,25%, il ne semble pas très utile avec le niveau de précision que j'ai en ce moment .