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 .
Réponses:
Si possible, je suggère de sélectionner un microcontrôleur qui prend en charge une opération de compteur à l'aide des entrées de minuterie; plutôt que d'incrémenter manuellement un compteur à l'intérieur d'un ISR (qui à des fréquences élevées finit rapidement par saturer l'activité du microcontrôleur), vous autorisez le matériel à gérer le comptage. À ce stade, votre code devient simplement une question d'attendre votre interruption périodique, puis de calculer la fréquence.
Pour étendre la plage et rendre le compteur de fréquence plus généralisé (en supprimant le besoin de plusieurs plages au détriment d'un peu plus de travail pour le MCU), vous pouvez utiliser la technique suivante.
Sélectionnez un taux d'interruption périodique qui permet une précision de mesure à la fréquence d'entrée la plus élevée; cela devrait prendre en compte la taille de votre compteur (vous devez sélectionner la période de la minuterie de sorte que le compteur de la minuterie ne déborde pas à la fréquence d'entrée maximale). Pour cet exemple, je suppose que la valeur du compteur d'entrée peut être lue à partir de la variable "timer_input_ctr".
Inclure une variable pour compter les interruptions périodiques (doit être initialisée à 0 au démarrage); pour cet exemple, je ferai référence à cette variable comme "isr_count". La période d'interruption est contenue dans la constante "isr_period".
Votre interruption périodique doit être implémentée en tant que (pseudo-code C):
Évidemment, cet exemple grossier repose sur des calculs en virgule flottante qui peuvent ne pas être compatibles avec les microcontrôleurs bas de gamme, il existe des techniques pour surmonter cela, mais elles sortent du cadre de cette réponse.
la source
Vous voudrez peut-être envisager d'avoir deux (ou plus) plages. Les problèmes de capture de fréquences très basses sont quelque peu différents de ceux des fréquences supérieures. Comme vous l'avez déjà remarqué, dans le haut de gamme, vous avez des problèmes de débordement de compteur.
Mais pensez au bas de votre plage, votre précision souffrira de ne pas avoir suffisamment de comptes dans le registre. Je ne sais pas si vous voulez vraiment faire la différence entre 0,25 Hz et 0,5 Hz, mais si vous le faites, vous devrez en fait compter quatre secondes pour le faire.
En outre, la spécification d'une résolution plate de 0,25 Hz, strictement interprétée, signifie que vous seriez en mesure de discerner 500 000,00 Hz de 500 000,25 Hz, ce qui est un degré de précision assez élevé.
Pour ces raisons, la conception pour des plages distinctes pourrait atténuer le problème de taille de compteur. Tirer des nombres au hasard, par exemple, pour le bas de gamme, disons 0 à 100 Hz, comptez pour des intervalles de 10 secondes, et vous obtenez une résolution de 0,1 Hz, et votre compteur n'a besoin que d'aller jusqu'à 1000, pas même 10 bits. Ensuite, de 100 Hz à 10 kHz, comptez pour des intervalles de 1 seconde; vous n'obtenez qu'une résolution de 1 Hz, mais votre compteur n'a besoin que de fonctionner jusqu'à 10 000 encore plus petit que 16 bits. La plage supérieure de 10 kHz à 1 MHz pourrait compter pour seulement 0,01 s, et le nombre maximal ne serait toujours que de 10 000 et bien que votre résolution soit de 100 Hz, ce serait une précision raisonnable.
la source
Vous pouvez mélanger un compteur matériel et logiciel en comptant les débordements du compteur matériel dans un ISR.
Le comptage de chaque front du signal dans un ISR sera trop lent pour un signal à 1 MHz. Je pense que vous pourriez faire jusqu'à environ 50 kHz de cette façon.
la source
Au lieu de faire un compteur de 1 seconde, faites-en un compteur de 0,1 seconde et multipliez le compte par 10?
S'il s'agit simplement de stocker le numéro de compteur, ne pouvez-vous pas utiliser un code supplémentaire pour savoir quand le compteur est sur le point de déborder et écrire dans un autre emplacement mémoire pour conserver le décompte?
la source
Ne pouvez-vous pas simplement utiliser la capture d'entrée d'un temporisateur 16 bits et les interruptions de débordement (plus une variable) pour effectuer la mesure? Voici comment je le ferais avec l'ATTiny24A avec AVR-GCC (non testé et potentiellement buggé bien sûr):
... en tout cas, ça compile :)
EDIT J'ai regardé la sortie du fichier lss de mon code, et le code généré a trop d'instructions pour ne pas se déclencher à 1 MHz avec une horloge de 8 MHz ... même l'incrémentation simple d'une ligne dans le TIM1_OVF_vect génère 19 instructions! Donc, pour gérer les événements à 1 MHz, vous devez certainement optimiser, enregistrer probablement allouer des trucs (probablement num_overflows et capture_value_ticks), utiliser l'assembleur en ligne (voler les trucs importants du fichier lss), et déplacer le traitement hors des interruptions et dans le principal boucle autant que possible.
la source
Publier ce code comme alternative par la suggestion de @ timrorr à mon post précédent. Cela compile pour l'ATTiny24A en utilisant la norme de langage c99, mais je ne l'ai pas testé de quelque manière que ce soit.
C'est une belle petite utilisation des capacités matérielles du Timer1 et libère une tonne de cycles de traitement par rapport à mon article d'origine.
la source
En utilisant des pré-détartreurs, même la mesure GHz peut être obtenue. Il s'agit d'un simple fréquencemètre de 40 MHz avec ATMEL AVR AT90S2313: http://www.myplace.nu/avr/countermeasures/index.htm
Voici quelques autres projets similaires:
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
la source