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:
Wo wie die erwartete Ausgabe ungefähr so aussehen sollte:
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:
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:
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.
quelle
Antworten:
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.
quelle
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.
Wenn sich der Hysteresewert im hohen Schwellenwert befindet, sollten Sie auf einen negativen Flankeninterrupt warten.
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.
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.
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.
quelle
Dieser Effekt ähnelt dem Kontaktsprung und kann durch dieselben Entprellungstechniken gemindert werden, die Sie für Drucktasten verwenden würden.
Td
Td
, ignorieren Sie den aktuellen Interruptquelle