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), der nur in der Amplitude (mit einem Potentiometer) variiert werden kann. Der positive Eingang des Komparators wird auf Vcc/2 gesetzt. 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 Totzonen) am Ausgang bekomme, das so aussieht:
Wobei die erwartete Ausgabe ungefähr so aussehen sollte:
Dinge, die ich bisher ausprobiert habe:
Verwenden des internen Komparators von atmega2560. Verwendung eines externen Komparators. Einführung von Hysterese mit Software und Schmitt-Trigger-Schaltung. Versuchte verschiedene Eingabe-Setups, einschließlich fester Referenz-Setup und Data-Slicer-Setup. Probieren Sie verschiedene atmega2560 aus. Probieren Sie verschiedene Taktraten aus.
Einige Lösungen waren stabiler als andere, aber keine davon war auch nur annähernd akzeptabel. Ich habe mich mit der bisher stabilsten Konfiguration niedergelassen:
Bei diesem Setup verbessern/verändern gewisse Dinge die Stabilität, sind aber noch lange nicht perfekt:
Änderung des Wertes von R5 zur Erhöhung der Hysterese. C2 komplett entfernen (keine Ahnung warum). Berühren von Drähten auf dem Steckbrett (etliche davon nebeneinander). Netzteile von extern auf USB umschalten und umgekehrt.
An diesem Punkt ist es entweder Rauschen, mein DAC, mit dem ich die Sinuswelle erzeuge, oder ich mache etwas ganz Grundlegendes falsch. Diese Schaltung hat für andere Leute ohne Probleme funktioniert, also muss etwas mit meiner Konfiguration oder Umgebung nicht stimmen.
Wenn jemand irgendwelche Vorschläge hat, würde ich Ihre Zeit sehr schätzen.
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-measure-library/
AKTUALISIEREN:
Ich habe alle Ihre Vorschläge ausprobiert, keiner von ihnen hat funktioniert, bis auf einen. Das Löschen der Interrupt-Flags oder das Deaktivieren der Interrupts innerhalb oder außerhalb der ISR hatte keine wirkliche Wirkung. Ich scheine falsch zu verstehen, wie das Komparatorregister des Chips tatsächlich funktioniert.
Wie ich anfangs 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, dann verwenden Sie 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, hat der Komparator zwei Ausgänge, ACO und ACIS0+ACIS1. ACO wird gesetzt, wenn + Eingang > – Eingang, gelöscht, wenn + Eingang < – Eingang. ACIS0 + ACIS1 sind Flankenauswahlbits.
Was ich anfangs tat, war, den Kantentyp in meinem ISR zu überprüfen. Ich habe die ISR stattdessen folgendermaßen 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 der Ausgang verhielt sich einwandfrei (genau wie im zweiten Bild). Dann fuhr ich fort, die Breite der Impulse zu messen, aber die Ergebnisse waren nicht großartig. Intensives Toggeln auf meinem LCD-Display, Zahlen springen auf zufällige Werte oder bleiben auf 0, obwohl ich ein sauberes Signal habe. Ich habe meinen Code viele Male unter Verwendung unterschiedlicher Bedingungen neu geschrieben. Die einzige halbstabile Lösung, die ich bisher habe, ist diese:
#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, dass ich in 1/3 der Fälle den richtigen Wert erhalte. In den anderen 2/3 der Fälle ist es entweder die Hälfte des korrekten Werts oder ein zufälliger Wert. Ich habe versucht, die Registerbits des Timers für bedingte Anweisungen sowie die Registerbits des Komparators in meiner ISR zu verwenden. Dies ist die einzige Konfiguration, die funktioniert.
Später am Tag habe ich stattdessen einen externen Komparator mit identischem Setup und identischer Quelle verwendet (mit Ausnahme aller Leitungen, die sich auf den Komparator beziehen). Sein Ausgang wurde in den Eingangserfassungsstift eingespeist und funktionierte wie beabsichtigt (benötigte 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 verhält. Ich habe viele Beiträge und Anleitungen dazu gelesen, verschiedene Bibliotheken gelesen und versucht, sie ohne akzeptables Ergebnis zu imitieren. Das Datenblatt hat nur 5 Seiten über die gesamte 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 Eingaben haben, wird dies sehr geschätzt.
Ich habe gelesen, dass Sie einen DAC verwenden, um das Sinuswellensignal zu erzeugen. DAC-Ausgänge können bei Änderungen des Ausgangszustands Störungen verursachen, daher sollten Sie auf jeden Fall eine analoge Filterung auf den DAC-Ausgang anwenden, bevor Sie ihn in Ihre Komparatorschaltung einspeisen. Dies kann dabei helfen, einige der doppelten Interrupt-Trigger zu verhindern, die wahrscheinlich auftreten.
Ich möchte auch anmerken, dass Sie für diese Art von Problem wirklich einen externen Komparator verwenden möchten, damit Sie die Hysterese mit Widerständen ohne Verwendung einer Softwareinteraktion anwenden können. Dies ermöglicht auch eine bessere Problemisolierung, da Sie den Ausgang des Komparators direkt überwachen können.
Der letzte Kommentar bezieht sich auf die Art der Hysterese, die Sie verwenden. Es ist ein bisschen schwer zu erkennen, welches Schema Sie verwenden, aber beachten Sie, dass Sie ein Verhalten wünschen, das dies tut: Sie möchten die Hysterese, die die Schwellenspannung in die ENTGEGENGESETZTE Richtung zieht, als das Signal übergeht. Für eine steigende Flanke soll der Schwellenwert also etwas höher als der Nullpunkt sein, und wenn sich der Zustand ändert, wird der Schwellenwert auf einen niedrigeren Pegel gezogen.
Das Problem bei diesem Szenario ist, dass es eine Zeitverzögerung zwischen dem Umschalten des Komparators und der Bearbeitung des Interrupts bis zu dem Punkt gibt, an dem 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 diese Rechteckwelle auf Ihrem Oszilloskop hat.
Unter Berücksichtigung dieser beiden Faktoren besteht eine hohe Wahrscheinlichkeit, dass Sie bei bestimmten Eingangspegeln mehrere Flanken vom Komparator erhalten, bevor Sie die erste verarbeiten können. Das Prüfen, um zu sehen, was der Komparatorzustand während dieses Interrupt-Handlers ist, wird nicht viel helfen, 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 in etwa so funktionieren.
Wenn sich der Hysteresewert im Zustand hoher Schwelle befindet, sollten Sie auf einen Interrupt mit negativer Flanke warten.
Wenn der Interrupt mit negativer Flanke eintrifft, schalten Sie die Hysterese auf den niedrigen Wert um, warten Sie einige Zyklen, löschen Sie dann alle anstehenden Interrupts und beginnen Sie mit dem Warten auf einen Interrupt mit positiver Flanke.
Wenn der Interrupt mit positiver Flanke eintrifft, schalten Sie den Hysteresestift zurück auf den hohen Wert, warten Sie einige Zyklen, löschen Sie alle anstehenden Interrupts und beginnen Sie erneut mit dem Warten auf einen Interrupt mit negativer Flanke.
Ab Schritt 1 wiederholen.
Übrigens bin ich nicht sehr daran interessiert, wie Sie die Komparatorreferenz als Vorspannung für das Signal verwenden. Das führt zu einem kleinen Übersprechen sowohl vom Signal zur Referenz als auch von der Hysterese zum Signal, insbesondere bei niederfrequenten Signalen. Zugegeben, bei diesen Werten sollte dieser Effekt gering sein, aber für die 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 an und löschen alle anstehenden Interrupts, bevor Sie zurückkehren. (Beachten Sie, dass das Ändern des Interrupt-Steuerregisters selbst Interrupts erzeugen kann.)
Ich weiß nicht, ob der Atmega wiedereintretende Interrupts durchführt, dh ob eine nachfolgende Flanke den noch laufenden Handler von der vorherigen Flanke unterbricht. Wenn dies der Fall ist, müssen Sie die Parallelität entsprechend behandeln.
Ich bin mir nicht sicher, wofür der PORTC-Teil gedacht ist, aber er muss wahrscheinlich in den qualifizierten Teil verschoben werden.
Dieser Effekt ähnelt dem Kontaktprellen und kann durch die gleichen Entprelltechniken gemildert werden, die Sie für Drucktasten verwenden würden.
Td
Td
, ignoriere den aktuellen InterruptDieser Designhinweis von TI erklärt genau das Problem eines verrauschten Signals und die Notwendigkeit, eine angemessene Hysterese hinzuzufügen. Dies geschieht natürlich mit einem externen Komparator, wie von @Michael Karas empfohlen, und löst auch das Mehrfachzünden bei negativen Eingangssignalübergängen, dh das Problem des "Kontaktprellens", wie von @Trevor_G und @Dmitry Grigoryev in ihren Antworten beschrieben https:// www.ti.com/lit/ug/tidu020a/tidu020a.pdf?ts=1621988309888&ref_url=https%253A%252F%252Fwww.google.com%252F
Benutzer16222
Andi aka
Shibalicious
Shibalicious
Andi aka
Shibalicious
Andi aka
Shibalicious
Andi aka
Shibalicious
Trevor_G
Shibalicious
Trevor_G
Jasen
Shibalicious
Shibalicious
Trevor_G