Zufälliges und unvorhersehbares Verhalten des analogen Komparators


10

Ich arbeite an einem relativ "einfachen" Projekt, bei dem ich die Frequenz einer Sinuswelle messen muss, die sich in Amplitude und Frequenz ändert. Zur Vereinfachung habe ich vorerst nur einen Sinuswelleneingang mit fester Frequenz (27 Hz) (negativer Eingang des Komparators), dessen Amplitude nur mit einem Potentiometer variiert werden kann. Der positive Eingang des Komparators ist auf Vcc / 2 eingestellt. Der Ausgang des Komparators wird dann in das Eingangserfassungsregister des atmega2560-Mikrocontrollers eingespeist, um die Frequenz zu messen.

Das Problem ist, dass ich bei bestimmten Amplituden des Eingangssignals ein ziemlich intensives Umschalten (oder manchmal tote Bänder) am Ausgang bekomme, was so aussieht:

Geben Sie hier die Bildbeschreibung ein

Wo wie die erwartete Ausgabe ungefähr so ​​aussehen sollte:

Geben Sie hier die Bildbeschreibung ein

Dinge, die ich bisher versucht habe:

Verwendung des internen Komparators von atmega2560. Verwendung eines externen Komparators. Einführung der Hysterese mithilfe von Software und Schmitt-Triggerschaltung. Versuchte verschiedene Eingabe-Setups, einschließlich fester Referenz-Setups und Data Slicer-Setups. Probieren Sie verschiedene atmega2560 aus. Verschiedene Taktraten ausprobieren.

Einige Lösungen waren stabiler als andere, aber keine von ihnen war annähernd akzeptabel. Ich habe mich mit der bisher stabilsten Konfiguration zufrieden gegeben:

Geben Sie hier die Bildbeschreibung ein

Mit diesem Setup verbessern / verändern bestimmte Dinge die Stabilität, sind jedoch bei weitem nicht perfekt:

Ändern des Werts von R5 zur Erhöhung der Hysterese. C2 vollständig entfernen (keine Ahnung warum). Berühren von Drähten auf dem Steckbrett (einige davon nebeneinander). Schalten von Netzteilen von extern auf USB und umgekehrt.

Zu diesem Zeitpunkt ist es entweder Rauschen, mein DAC, mit dem ich die Sinuswelle generiere, oder ich mache etwas sehr Grundlegendes falsch. Diese Schaltung hat für andere Personen ohne Probleme funktioniert, daher muss etwas mit meiner Konfiguration oder Umgebung nicht stimmen.

Wenn jemand Vorschläge hat, würde ich mich sehr über Ihre Zeit freuen.

Hier ist meine minimale Quelle:

#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);
    }
}

Hier ist auch der Link zum Schaltplan und zur Bibliothek selbst:

http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/

AKTUALISIEREN:

Ich habe alle Ihre Vorschläge ausprobiert, keiner von ihnen hat funktioniert, außer einem. Das Löschen der Interrupt-Flags oder das Deaktivieren der Interrupts innerhalb oder außerhalb des ISR hatte keine wirklichen Auswirkungen. Ich scheine falsch zu verstehen, wie das Komparatorregister des Chips tatsächlich funktioniert.

Wie ich ursprünglich erwähnt hatte, wollte ich die Eingangserfassung verwenden, um die Frequenz einer Rechteckwelle zu messen, die von einer Sinuswelle abgeleitet wurde. Der Ausgang des Komparators wird in den Eingangserfassungsstift eingespeist und verwendet dann Timer, um die Periode einfach zu messen.

Hier ist das analoge Komparatordiagramm von atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , Seite 265:

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, verfügt der Komparator über zwei Ausgänge, ACO und ACIS0 + ACIS1. ACO wird eingestellt, wenn + Eingang> - Eingang, gelöscht, wenn + Eingang <- Eingang. ACIS0 + ACIS1 sind Kantenauswahlbits.

Ich habe anfangs den Kantentyp in meinem ISR überprüft. Ich habe stattdessen den ISR geändert:

    ISR(ANALOG_COMP_vect) {

     if (!(ACSR &  (1<<ACO))) { // + < -
         /* Set PIND5 to 0V */
         PORTD &= ~(1 << PIND5);
    }
    else  {
       /* Set PIND5 to 5V */
       PORTD |= (1 << PIND5);
    }
}

Und die Ausgabe hat sich einwandfrei verhalten (genau wie im zweiten Bild). Dann habe ich die Breite der Impulse gemessen, aber die Ergebnisse waren nicht großartig. Intensives Umschalten auf meinem LCD-Display, Zahlen, die auf zufällige Werte springen oder trotz sauberem Signal bei 0 bleiben. Ich habe meinen Code viele Male unter verschiedenen Bedingungen umgeschrieben. Die einzige halbstabile Lösung, die ich bisher habe, ist folgende:

#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);
}

Mit halbstabil meine ich, ich bekomme 1/3 der Fälle den richtigen Wert. Die anderen Male 2/3 der Fälle ist es entweder die Hälfte des korrekten Wertes oder ein zufälliger Wert. Ich habe versucht, Timer-Registerbits für bedingte Anweisungen sowie Komparatorregisterbits in meinem ISR zu verwenden. Dies ist die einzige Konfiguration, die funktioniert.

Was ich später am Tag tat, war, stattdessen einen externen Komparator mit identischem Setup und identischer Quelle zu verwenden (mit Ausnahme aller Zeilen, die sich auf den Komparator beziehen). Sein Ausgang wurde in den Eingangserfassungsstift eingespeist und funktionierte wie vorgesehen (brauchte nicht einmal eine Hysterese).

An dieser Stelle kann ich sagen, dass ich es mit einem externen Komparator gelöst habe, aber ich habe keine Ahnung, warum sich der interne nicht selbst verhält. Ich habe viele Beiträge und Anleitungen dazu gelesen, verschiedene Bibliotheken gelesen und versucht, sie ohne akzeptables Ergebnis nachzuahmen. Das Datenblatt enthält nur 5 Seiten der gesamten Komparatoreinheit. Ich habe es viele Male erneut gelesen und sehe nicht, was ich falsch mache.

Ich würde gerne herausfinden, wie man es richtig benutzt, aber wenn das fehlschlägt, habe ich ein Backup. Wenn Sie weitere Informationen haben, wird dies sehr geschätzt.


4
für den Anfang ... fügen Sie einen 1M Widerstand zwischen dem Ausgang und dem + ve Eingang hinzu. Dies ist, was Hysterese erzeugt, nicht Ihr R5 ... das ändert nur die Referenz
JonRB

1
Wie können Sie Scope-Bilder der Ausgabe von einem Komparator erstellen, der sich im Chip befindet und nicht zugänglich ist?
Andy aka

2
Deaktivieren Sie weitere Interrupts, wenn Sie einen ISR eingeben? Möglicherweise müssen Sie - es könnte sein, dass die meisten ISRs doppelte Treffer erhalten.
Andy aka

1
Wie schalten Sie den Hysteresestift um und qualifizieren ihn anhand des aktuellen Werts? Die Verzögerung zwischen dem Interrupt und dem Umschalten kann sich auf Sie auswirken.
Trevor_G

1
In Ihrem Schaltplan ist die interne Kapazität zwischen Pin5 und Pin6 nicht dargestellt. Können Sie stattdessen den internen Pull-up an Pin7 verwenden, um Ihre Hysterese zu erzeugen?
Jasen

Antworten:


13

Ich habe gelesen, dass Sie einen DAC verwenden, um das Sinuswellensignal zu erzeugen. DAC-Ausgänge können bei Änderungen des Ausgangszustands fehlerhaft sein. Daher sollten Sie auf jeden Fall eine analoge Filterung auf den DAC-Ausgang anwenden, bevor Sie ihn in Ihre Komparatorschaltung einspeisen. Dies kann dazu beitragen, einige der wahrscheinlich auftretenden Doppelinterrupt-Trigger zu verhindern.

Ich würde auch kommentieren, dass Sie wirklich einen externen Komparator für diese Art von Problem verwenden möchten, damit Sie die Hysterese mit Widerständen ohne die Verwendung einer Software-Interaktion anwenden können. Dies ermöglicht auch eine bessere Problemisolierung, da Sie die Ausgabe des Komparators direkt überwachen können.

Der letzte Kommentar bezieht sich auf die Art der von Ihnen verwendeten Hysterese. Es ist etwas schwierig, genau zu erkennen, welches Schema Sie verwenden. Beachten Sie jedoch, dass Sie ein Verhalten wünschen, das dies bewirkt: Sie möchten die Hysterese, die die Schwellenspannung in die OPPOSITE-Richtung zieht, als das Signal übergeht. Für eine ansteigende Flanke soll der Schwellenwert also etwas höher als der Nullpunkt sein. Wenn sich der Status ändert, wird der Schwellenwert auf einen niedrigeren Wert gezogen.


1
+1 für die zusätzliche Beschreibung, wie die Hystereserichtung funktionieren soll. Absatz 2 ist ein guter Rat, aber es ist auch in Ordnung, dies intern zu tun, vorausgesetzt, es wird richtig gemacht, was in diesem Beispiel nicht der Fall zu sein scheint.
Trevor_G

@ Trevor_G -: ^)
Michael Karas

1
@ Hypomania - Ich weiß, dass Sie den einzelnen Timer im ISR lesen können. Wenn der Timer jedoch nicht doppelt gepuffert ist, so dass ein Ausgangsregister die Zählung von einem Trigger hält, während der Timer selbst weiter zählen kann, muss der Timer angehalten werden, damit Sie ihn lesen und nach dem Lesen wieder aktivieren können . Viele MCU-Timer sind auf diese Weise nicht doppelt gepuffert, und daher geht die Verarbeitungszeit zum Erreichen des ISR bei erneuter Aktivierung des Timers bei der Periodenzeitmessung für den nächsten Halbzyklus verloren. Es hängt bis zu einem gewissen Grad davon ab, wie schnell der Timer getaktet wird (Fortsetzung)
Michael Karas

1
(Fortsetzung von oben), aber Sie möchten nie in der Situation sein, dass Sie einen Zählwert lesen, wenn gleichzeitig eine Uhr kommen könnte, um die Zählung zu ändern. Ich habe die von Ihnen verwendete MCU nicht untersucht, um festzustellen, ob Ihr Timer bei einem Trigger-Capture-Ereignis doppelt gepuffert ist oder nicht.
Michael Karas

1
@Hypomania - Aus einer Laune heraus habe ich mir Ihr verknüpftes AVR-MCU-Datenblatt angesehen und festgestellt, dass die Timer-Eingabeerfassungsfunktion doppelt gepuffert ist !! Tatsächlich sieht der Timer in diesen Teilen ziemlich robust aus. Es ist fast 15 Jahre her, seit ich AVR-Teile verwendet habe.
Michael Karas

6

Das Problem bei diesem Szenario ist, dass zwischen dem Umschalten des Komparators und dem Interrupt, der behandelt wird, eine Zeitverzögerung besteht, bis Sie den "Hysterese" -Pin umschalten.

Ihr Hystereseband ist für diesen Signalpegel auch ziemlich klein, wenn man bedenkt, wofür Sie es verwenden. Besonders wenn ich sehe, wie viel Rauschen auf dieser Rechteckwelle auf Ihrem Zielfernrohr ist.

In Anbetracht dieser beiden Faktoren besteht eine hohe Wahrscheinlichkeit, dass Sie bei bestimmten Eingangspegeln mehrere Kanten vom Komparator erhalten, bevor Sie die erste verarbeiten können. Das Überprüfen, ob der Komparatorstatus während dieses Interrupt-Handlers vorliegt, hilft nicht viel, da er sich in beiden Zuständen befinden kann.

Leider haben Sie in der Frage nicht detailliert beschrieben, wie der Handler funktioniert.

Ihr Handler sollte jedoch so etwas arbeiten.

  1. Wenn sich der Hysteresewert im hohen Schwellenwert befindet, sollten Sie auf einen negativen Flankeninterrupt warten.

  2. Wenn der negative Flankeninterrupt eintrifft, schalten Sie die Hysterese auf den niedrigen Wert um, warten Sie einige Zyklen, löschen Sie dann alle anstehenden Interrupts und warten Sie auf einen positiven Flankeninterrupt.

  3. Wenn der positive Flankeninterrupt eintrifft, schalten Sie den Hysteresestift wieder auf den hohen Wert zurück, warten Sie einige Zyklen, löschen Sie alle anstehenden Interrupts und warten Sie erneut auf einen negativen Flankeninterrupt.

  4. Wiederholen Sie ab Schritt 1.

Übrigens bin ich nicht besonders daran interessiert, wie Sie die Komparatorreferenz als Vorspannung für das Signal verwenden. Dies führt zu einem kleinen Übersprechen sowohl vom Signal zur Referenz als auch von der Hysterese zum Signal, insbesondere bei niederfrequenten Signalen. Bei diesen Werten sollte dieser Effekt gering sein, aber aus Gründen der Reinheit wäre eine separate Vorspannung des Signals besser.

EDIT: Re Ihren Code.

In der else-Anweisung ändern Sie die Interrupt-Flanke, bevor Sie die Hysterese einstellen.

In keinem Fall halten Sie anstehende Interrupts an und löschen sie, bevor Sie zurückkehren. (Beachten Sie, dass durch Ändern des Interrupt-Steuerregisters selbst Interrupts erstellt werden können.)

Ich weiß nicht, ob der Atmega Interrupts erneut einführt, dh ob eine nachfolgende Flanke den noch laufenden Handler von der vorherigen Flanke unterbricht. In diesem Fall müssen Sie die Parallelität angemessen behandeln.

Ich bin nicht sicher, wofür das PORTC-Teil gedacht ist, aber es muss wahrscheinlich in das qualifizierte Teil verschoben werden.


Vielen Dank, ich werde morgen Ihre Vorschläge ausprobieren und Ihnen ein Update geben. Für meinen ISR habe ich eine if-else if-Anweisung für das genaue Szenario, das Sie beschrieben haben, ohne das Warten.
Shibalicious

1
@Hypomania Sie sollten Ihre Frage bearbeiten und Ihren Interrupt-Handler-Code veröffentlichen, damit die Leute sehen können, ob Sie irgendwo etwas falsch machen. Es kann jedoch nur Rauschen sein. Ihre Oszilloskopspuren sehen so aus, als ob dort weit mehr als 50 mV Rauschen auftreten. Immer noch mit Rauschen würde ich erwarten, dass es sich selbst korrigiert, alles mit gelegentlichen zusätzlichen Impulsen an den Übergängen.
Trevor_G

Das habe ich auch erwartet. Werde es so schnell wie möglich tun.
Shibalicious

1
@ Hypomania siehe bearbeiten
Trevor_G

1
@ Hypomania Weil Sie einen weiteren Interrupt zwischen diesen beiden Befehlen bekommen könnten. Ich habe auch die Bearbeitung bearbeitet ...
Trevor_G

3

Dieser Effekt ähnelt dem Kontaktsprung und kann durch dieselben Entprellungstechniken gemindert werden, die Sie für Drucktasten verwenden würden.

  • Legen Sie die Entprellzeit fest Td
  • Behalten Sie den Zeitstempel des letzten Flankeninterrupts in einer Variablen
  • Wenn die Zeit zwischen dem aktuellen und dem letzten Interrupt kürzer ist als Td, ignorieren Sie den aktuellen Interrupt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.