Je travaille sur un projet relativement "simple" où je dois mesurer la fréquence d'une onde sinusoïdale qui varie en amplitude et en fréquence. Pour simplifier les choses, pour l'instant, je n'ai qu'une entrée à onde sinusoïdale à fréquence fixe (27Hz) (entrée négative du comparateur) qui ne peut varier qu'en amplitude (à l'aide d'un potentiomètre). L'entrée positive du comparateur est réglée sur Vcc / 2. La sortie du comparateur est ensuite introduite dans le registre de capture d'entrée du microcontrôleur atmega2560 pour mesurer la fréquence.
Le problème est qu'à certaines amplitudes du signal d'entrée, j'obtiens un basculement assez intense (ou parfois des bandes mortes) sur la sortie qui ressemble à ceci:
Où la sortie attendue devrait ressembler à ceci:
Ce que j'ai essayé jusqu'à présent:
Utilisation du comparateur interne de l'atmega2560 interne. Utilisation d'un comparateur externe. Présentation de l'hystérésis à l'aide du logiciel et du circuit de déclenchement de Schmitt. J'ai essayé diverses configurations d'entrée, y compris la configuration de référence fixe et la configuration du trancheur de données. Essayer différents atmega2560. Essayer différentes vitesses d'horloge.
Certaines solutions étaient plus stables que d'autres, mais aucune d'entre elles n'était loin d'être acceptable. Je me suis installé avec la configuration la plus stable à ce jour:
Avec cette configuration, certaines choses améliorent / modifient la stabilité, mais toujours loin d'être parfaites:
Modification de la valeur de R5 pour augmenter l'hystérésis. Suppression complète de C2 (aucune idée pourquoi). Toucher des fils sur la planche à pain (un certain nombre d'entre eux côte à côte). Commutation des alimentations de l'extérieur vers l'USB et vice versa.
À ce stade, c'est soit du bruit, mon DAC avec lequel je génère l'onde sinusoïdale, soit je fais quelque chose de très fondamental de manière incorrecte. Ce circuit a fonctionné pour d'autres personnes sans aucun problème, donc quelque chose ne va pas avec ma configuration ou mon environnement.
Si quelqu'un a des suggestions, j'apprécierais grandement votre temps.
Voici ma source minimale:
#include <avr/io.h>
void init(void);
void init(void) {
/* Setup comparator */
ACSR = (1 << ACIE) | (1 << ACIS1);
/* Initialize PORTD for PIND5 */
DDRD = 0x00;
PORTD = 0x00;
/* Enable global interrupts */
sei();
}
int main(void) {
init();
while (1) {}
}
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACIS0))) { //comparator falling edge
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
ACSR |= (1<<ACIS0); //set next comparator detection on rising edge
}
else {
ACSR &= ~(1<<ACIS0); //set next comparator detection on falling edge
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
En outre, voici le lien vers le schéma de circuit et la bibliothèque elle-même:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
MISE À JOUR:
J'ai essayé toutes vos suggestions, aucune n'a fonctionné sauf une. La suppression des indicateurs d'interruption ou la désactivation des interruptions à l'intérieur ou à l'extérieur de l'ISR n'a pas vraiment eu d'effet. Je semble mal comprendre comment fonctionne réellement le registre comparateur de la puce.
Comme je l'avais mentionné initialement, j'allais utiliser la capture d'entrée pour mesurer la fréquence d'une onde carrée dérivée d'une onde sinusoïdale. La sortie du comparateur est introduite dans la broche de capture d'entrée, puis utilisez des minuteries pour mesurer la période, simplement.
Voici le diagramme de comparaison analogique de atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , page 265:
Comme vous pouvez le voir, le comparateur a deux sorties, ACO et ACIS0 + ACIS1. ACO est défini lorsque + entrée> - entrée, effacé lorsque + entrée <- entrée. ACIS0 + ACIS1 sont des bits de sélection de front.
Au départ, je vérifiais le type de bord dans mon ISR. J'ai changé l'ISR à la place:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
Et la sortie s'est comportée parfaitement (comme dans la deuxième image). Ensuite, j'ai commencé à mesurer la largeur des impulsions, mais les résultats n'étaient pas excellents. Basculement intense sur mon écran LCD, les nombres sautant à des valeurs aléatoires ou restant à 0, malgré un signal propre. J'ai réécrit mon code plusieurs fois en utilisant des conditions différentes, la seule solution semi-stable que j'ai eu jusqu'à présent est la suivante:
#include <avr/io.h>
#include <util/delay.h>
#include "UART.h"
void init(void);
volatile uint16_t y = 0;
volatile uint16_t x = 0;
volatile uint16_t current_value = 0;
volatile uint16_t previous_value = 0;
volatile uint16_t total = 0;
void init(void) {
/* Normal mode, 64 prescaler, Rising Edge trigger, Input Capture */
TCCR1A = 0;
TCCR1B = (1 << CS10) | (1 << CS11) | (1 << ICES1);
TIMSK1 = (1 << ICIE1);
ACSR = (1 << ACIC);
ADCSRB = 0x00;
/* This port is used for simulating comparator's output */
DDRC = 0xFF;
PORTC = 0xFF;
DDRD = 0x00;
PORTD = 0x00;
USART_Init(UBRR_VALUE);
sei();
}
int main(void) {
init();
while (1) {
if (TCNT1 == 60000) {
/* Display the values on the LCD */
USART_Transmit(0xFE);
USART_Transmit(0x01);
USART_Transmit_Double(x+y);
}
}
}
ISR(TIMER1_CAPT_vect) {
//ACSR &= ~(1<<ACIC);
if (!(ACSR & (1 << ACO))) {
if (!(TCCR1B & (1 << ICES1))) { // check for falling edge
PORTD |= (1 << PIND5);
PORTC &= ~(1 << PINC1);
TCCR1B |= (1 << ICES1);
current_value = ICR1;
x = current_value - previous_value;
previous_value = current_value;
}
}
else {
if (TCCR1B & (1 << ICES1)) { // check for rising edge
PORTD &= ~(1 << PIND5);
PORTC |= (1 << PINC1);
TCCR1B &= ~(1 << ICES1);
current_value = ICR1;
y = current_value - previous_value;
previous_value = current_value;
}
}
//ACSR |= (1<<ACIC);
}
Par semi-stable, je veux dire, j'obtiens la valeur correcte 1/3 des fois. Les autres fois, 2/3 des fois, c'est soit la moitié de la valeur correcte, soit une valeur aléatoire. J'ai essayé d'utiliser les bits de registre du temporisateur pour les instructions conditionnelles ainsi que les bits de registre du comparateur dans mon ISR, c'est la seule configuration qui fonctionne.
Ce que j'ai fait plus tard dans la journée a été d'utiliser un comparateur externe à la place avec la même configuration et la même source (à l'exclusion de toutes les lignes liées au comparateur). Sa sortie a été introduite dans la broche de capture d'entrée et elle a fonctionné comme prévu (n'a même pas eu besoin d'hystérésis).
À ce stade, je peux dire que je l'ai résolu en utilisant un comparateur externe, mais je ne sais pas pourquoi celui interne ne se comporte pas lui-même. J'ai lu de nombreux articles et guides à ce sujet, lu différentes bibliothèques, essayé de les imiter sans aucun résultat acceptable. La fiche technique ne contient que 5 pages sur l'ensemble du comparateur, je l'ai relu plusieurs fois et je ne vois pas ce que je fais mal.
Je voudrais savoir comment l'utiliser correctement mais si cela échoue, j'ai une sauvegarde. Si vous avez d'autres commentaires, c'est grandement apprécié.
la source
Réponses:
J'ai lu que vous utilisez un DAC pour générer le signal d'onde sinusoïdale. Les sorties DAC peuvent brouiller les changements d'état de sortie, vous devez donc absolument appliquer un filtrage analogique à la sortie DAC avant de l'introduire dans votre circuit comparateur. Cela peut aider à empêcher certains déclencheurs de double interruption susceptibles de se produire.
Je voudrais également dire que vous voulez vraiment utiliser un comparateur externe pour ce type de problème afin de pouvoir appliquer l'hystérésis avec des résistances sans utiliser d'interaction logicielle. Cela permettra également une meilleure isolation des problèmes car vous pouvez surveiller directement la sortie du comparateur.
Le dernier commentaire concerne le type d'hystérésis que vous utilisez. Il est un peu difficile de voir exactement quel schéma vous utilisez, mais notez que ce que vous voulez, c'est un comportement qui fait cela: vous voulez l'hystérésis qui tire la tension de seuil dans la direction opposée que le signal est en transition. Donc, pour un front montant, vous voulez que le seuil soit un peu plus élevé que le point zéro, puis lorsque l'état change, le seuil est ramené à un niveau inférieur.
la source
Le problème avec ce scénario est qu'il y a un délai entre la commutation du comparateur et l'interruption traitée au point où vous commutez la broche "hystérésis".
Votre bande d'hystérésis est également assez petite pour ce niveau de signal compte tenu de l'utilisation que vous en faites. Surtout quand je vois la quantité de bruit sur cette onde carrée sur votre oscilloscope.
Avec ces deux facteurs à l'esprit, il y a une forte probabilité qu'à certains niveaux d'entrée, vous obtiendrez plusieurs fronts du comparateur avant de pouvoir gérer le premier. Vérifier pour voir quel est l'état du comparateur pendant ce gestionnaire d'interruption n'aidera pas beaucoup car il peut être dans l'un ou l'autre état.
Malheureusement, vous n'avez pas expliqué dans la question comment fonctionne le gestionnaire.
Votre gestionnaire devrait cependant travailler quelque chose comme ça.
Lorsque la valeur d'hystérésis à l'état de seuil haut, vous devez attendre une interruption de front négatif.
Lorsque ladite interruption de front négatif arrive, basculez l'hystérésis à la valeur basse, attendez quelques cycles, puis effacez toute interruption en attente et commencez à attendre une interruption de front positif.
Lorsque ladite interruption de front positif arrive, ramenez la broche d'hystérésis à la valeur élevée, attendez quelques cycles, supprimez toute interruption en attente et recommencez à attendre une interruption de front négatif.
Répétez à partir de l'étape 1.
BTW Je ne suis pas trop intéressé par la façon dont vous utilisez la référence du comparateur comme biais pour le signal. Il en résulte un peu de diaphonie à la fois du signal vers la référence et de l'hystérésis vers le signal, en particulier avec les signaux basse fréquence. Avec ces valeurs, cet effet devrait être faible, mais pour la pureté, un biais séparé sur le signal serait préférable.
EDIT: Re votre code.
Dans l'instruction else, vous modifiez le front d'interruption avant de définir l'hystérésis.
Dans aucun des cas, vous ne mettez en pause et n'effacez les interruptions en attente avant de revenir. (Notez que la modification du registre de contrôle des interruptions peut créer des interruptions de son propre chef.)
Je ne sais pas si l'Atmega fait des interruptions rentrantes, c'est-à-dire si un front suivant interrompra le gestionnaire en cours d'exécution depuis le front précédent. Si c'est le cas, vous devez gérer la concurrence de manière appropriée.
Je ne sais pas à quoi sert la partie PORTC, mais elle doit probablement passer à la partie qualifiée.
la source
Cet effet est similaire au rebond de contact et peut être atténué par les mêmes techniques de rebond que vous utiliseriez pour les boutons-poussoirs.
Td
Td
, ignorez l'interruption en coursla source