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.