Sto lavorando a un progetto relativamente "semplice" in cui devo misurare la frequenza di un'onda sinusoidale che varia in ampiezza e frequenza. Per semplificare le cose, per ora, ho solo un input a onda sinusoidale a frequenza fissa (27Hz) (input negativo del comparatore) che può essere variato solo in ampiezza (usando un potenziometro). L'ingresso positivo del comparatore è impostato su Vcc / 2. L'output del comparatore viene quindi immesso nel registro di acquisizione degli input del microcontrollore atmega2560 per misurare la frequenza.
Il problema è che a certe ampiezze del segnale di ingresso ottengo un'intensa commutazione (o talvolta bande morte) sull'uscita che assomiglia a questo:
Dove l'output atteso dovrebbe assomigliare a questo:
Cose che ho provato finora:
Utilizzo del comparatore interno di atmega2560 interno. Utilizzando un comparatore esterno. Presentazione dell'isteresi mediante software e circuito trigger di Schmitt. Ho provato varie configurazioni di input, tra cui la configurazione di riferimento fissa e la configurazione dell'affettatrice di dati. Prova diversi atmega2560. Prova di diverse velocità di clock.
Alcune soluzioni erano più stabili di altre, ma nessuna di esse era quasi accettabile. Finora ho optato per la configurazione più stabile:
Con questa configurazione, alcune cose migliorano / modificano la stabilità, anche se non sono assolutamente perfette:
Modifica del valore di R5 per aumentare l'isteresi. Rimozione completa di C2 (non ho idea del perché). Toccando i fili sulla breadboard (alcuni di essi uno accanto all'altro). Commutazione degli alimentatori da esterno a USB e viceversa.
A questo punto, o è il rumore, il mio DAC con cui sto generando l'onda sinusoidale o sto facendo qualcosa di fondamentale in modo errato. Questo circuito ha funzionato per altre persone senza problemi, quindi qualcosa deve essere sbagliato nella mia configurazione o ambiente.
Se qualcuno ha qualche suggerimento, apprezzerei molto il tuo tempo.
Ecco la mia fonte minima:
#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);
}
}
Inoltre, ecco il link allo schema elettrico e alla libreria stessa:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
AGGIORNARE:
Ho provato tutti i tuoi suggerimenti, nessuno ha funzionato tranne uno. La cancellazione dei flag di interruzione o la disabilitazione degli interrupt all'interno o all'esterno dell'ISR non ha avuto alcun effetto. Mi sembra di fraintendere come funzioni effettivamente il registro dei comparatori del chip.
Come ho accennato inizialmente, avrei usato la cattura dell'input per misurare la frequenza di un'onda quadra derivata da un'onda sinusoidale. L'output del comparatore viene inserito nel pin di acquisizione dell'input, quindi utilizzare i timer per misurare il periodo, in modo semplice.
Ecco il diagramma comparativo analogico di atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , pagina 265:
Come puoi vedere, il comparatore ha due uscite, ACO e ACIS0 + ACIS1. ACO è impostato quando + input> - input, cancellato quando + input <- input. ACIS0 + ACIS1 sono bit di selezione dei bordi.
Quello che stavo facendo inizialmente stava controllando il tipo di bordo nel mio ISR. Ho invece cambiato l'ISR in questo:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
E l'output si è comportato perfettamente (proprio come nella seconda foto). Quindi ho continuato a misurare la larghezza degli impulsi, ma i risultati non sono stati grandi. Commutazione intensa sul mio display LCD, numeri che saltano a valori casuali o restano a 0, nonostante abbiano un segnale pulito. Ho riscritto il mio codice molte volte usando condizioni diverse, l'unica soluzione semi-stabile che ho ottenuto finora è questa:
#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);
}
Con semi-stabile intendo, ottengo il valore corretto 1/3 delle volte. Le altre volte 2/3 delle volte è la metà del valore corretto o un valore casuale. Ho provato a usare i bit di registro del timer per le istruzioni condizionali, nonché i bit di registro del comparatore nel mio ISR, questa è l'unica configurazione che funziona.
Quello che ho fatto nel corso della giornata è stato utilizzare un comparatore esterno invece con la stessa configurazione e sorgente (escludendo tutte le linee relative al comparatore). Il suo output è stato inserito nel pin di acquisizione input e ha funzionato come previsto (non ha nemmeno bisogno di alcuna isteresi).
A questo punto posso dire di averlo risolto utilizzando un comparatore esterno, ma non ho idea del perché quello interno non si comporti da solo. Ho letto molti post e guide su questo, ho letto diverse librerie, ho cercato di imitarli senza risultati accettabili. Il foglio dati ha solo 5 pagine sull'intera unità di confronto, l'ho riletto molte volte e non vedo cosa sto facendo di sbagliato.
Vorrei scoprire come usarlo correttamente, ma se fallisce ho un backup. Se hai ulteriori suggerimenti, è molto apprezzato.