Comportement de comparateur analogique aléatoire et imprévisible

10

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:

entrez la description de l'image ici

Où la sortie attendue devrait ressembler à ceci:

entrez la description de l'image ici

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:

entrez la description de l'image ici

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:

entrez la description de l'image ici

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é.

Shibalicious
la source
4
pour commencer ... ajoutez une résistance de 1M entre la sortie et l'entrée + ve. CECI est ce qui crée l'hystérésis, pas votre R5 ... qui change juste la référence
JonRB
1
Comment pouvez-vous produire des images de portée de la sortie d'un comparateur qui est à l'intérieur de la puce et non accessible?
Andy aka
2
Désactivez-vous d'autres interruptions lorsque vous saisissez un ISR? Vous devrez peut-être - il se pourrait que la plupart des ISR obtiennent des doubles coups.
Andy aka
1
Comment basculez-vous la broche d'hystérésis et qualifiez-vous la valeur actuelle. Le délai entre l'interruption et la bascule peut être vissé avec vous.
Trevor_G
1
non indiquée dans votre schéma est la capacité interne entre la broche5 et la broche6, pouvez-vous utiliser le pull-up interne sur la broche7 pour faire votre hystérésis à la place?
Jasen

Réponses:

13

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.

Michael Karas
la source
1
+1 pour une description supplémentaire du fonctionnement de la direction d'hystérésis. Le paragraphe 2 est un bon conseil, mais le faire en interne est également correct, à condition qu'il soit bien fait, ce qui, dans cet exemple, ne semble pas être le cas.
Trevor_G
@Trevor_G -: ^)
Michael Karas
1
@Hypomania - Je sais que vous pouvez lire la minuterie unique dans l'ISR. Mais à moins que le temporisateur ne soit à double tampon de sorte qu'un registre de sortie contienne le compte d'un déclencheur tandis que le temporisateur lui-même peut continuer à compter, il devient alors nécessaire d'arrêter le temporisateur pour que vous puissiez le lire et le réactiver après l'avoir lu. . De nombreux temporisateurs MCU ne sont pas à double tampon comme ça et donc le temps de traitement pour entrer dans l'ISR au moment où le temporisateur est réactivé est du temps perdu sur la mesure de la période pour le prochain demi-cycle. Cela dépend dans une certaine mesure de la vitesse à laquelle le chronomètre est cadencé (suite)
Michael Karas
1
(suite de ci-dessus) mais vous ne voulez jamais être dans la situation où vous lisez une valeur de comptage alors qu'une horloge pourrait venir en même temps pour changer le comptage. Je n'ai pas recherché le MCU spécifique que vous utilisez pour voir si votre temporisateur est double tampon sur un événement de capture de déclenchement ou non.
Michael Karas
1
@Hypomania - Sur un coup de tête, j'ai regardé votre fiche technique AVR MCU liée et j'ai vu que la fonction de capture d'entrée du minuteur était à double tampon !! En fait, la minuterie de ces pièces semble assez robuste. Cela fait presque 15 ans que je n'utilise aucune pièce AVR.
Michael Karas
6

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.

  1. Lorsque la valeur d'hystérésis à l'état de seuil haut, vous devez attendre une interruption de front négatif.

  2. 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.

  3. 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.

  4. 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.

Trevor_G
la source
Merci beaucoup, je vais essayer vos suggestions demain et vous donner une mise à jour. Quant à mon ISR, j'ai une instruction if-else if pour le scénario exact que vous avez décrit, à l'exception de l'attente.
Shibalicious
1
@Hypomania, vous devez modifier votre question et publier votre code de gestionnaire d'interruption afin que les gens puissent voir si vous vous trompez quelque part. Cependant, il peut s'agir simplement de bruit, vos traces de portée semblent avoir plus de 50 mV de bruit. Toujours avec du bruit, je m'attendrais à ce qu'il se corrige, que ce soit avec des impulsions supplémentaires occasionnelles lors des transitions.
Trevor_G
C'est ce à quoi je m'attendais aussi. Le fera dès que possible.
Shibalicious
1
@Hypomania voir l'édition
Trevor_G
1
@Hypomania Parce que vous pourriez obtenir une autre interruption entre ces deux commandes. J'ai également édité le montage ...
Trevor_G
3

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.

  • Décidez du temps de rebond Td
  • conserver l'horodatage de la dernière interruption de front dans une variable
  • si le temps entre l'interruption en cours et la dernière est inférieur à Td, ignorez l'interruption en cours
Dmitry Grigoryev
la source