Comportamento del comparatore analogico casuale e imprevedibile


10

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:

inserisci qui la descrizione dell'immagine

Dove l'output atteso dovrebbe assomigliare a questo:

inserisci qui la descrizione dell'immagine

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:

inserisci qui la descrizione dell'immagine

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:

inserisci qui la descrizione dell'immagine

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.


4
per i principianti ... aggiungere una resistenza da 1 M tra l'uscita e l'ingresso + ve. QUESTO è ciò che crea l'isteresi, non la tua R5 ... che cambia solo il riferimento
JonRB

1
Come è possibile produrre immagini di ambito dell'output da un comparatore all'interno del chip e non accessibile?
Andy alias il

2
Stai disabilitando ulteriori interrupt quando inserisci un ISR? Potrebbe essere necessario: è possibile che la maggior parte degli ISR ​​ottenga doppi successi.
Andy alias il

1
Come si attiva il pin di isteresi e lo si qualifica in base al valore corrente. Il ritardo tra l'interruzione e la levetta potrebbe essere complicato.
Trevor_G,

1
non mostrato nel tuo schema è la capacità interna tra pin5 e pin6, puoi usare il pull-up interno sul pin7 per rendere invece la tua isteresi?
Jasen,

Risposte:


13

Ho letto che stai usando un DAC per generare il segnale sinusoidale. Le uscite DAC possono presentare anomalie ai cambiamenti dello stato di uscita, quindi è necessario applicare sicuramente un filtro analogico all'uscita DAC prima di immetterlo nel circuito del comparatore. Questo può aiutare a prevenire alcuni dei trigger di doppio interruzione che potrebbero verificarsi.

Vorrei anche commentare che vuoi davvero utilizzare un comparatore esterno per questo tipo di problema in modo da poter applicare l'isteresi con resistori senza l'uso di un'interazione software. Ciò consentirà inoltre un migliore isolamento dei problemi poiché è possibile monitorare direttamente l'output del comparatore.

L'ultimo commento riguarda il tipo di isteresi che si sta utilizzando. È un po 'difficile vedere esattamente quale schema stai usando, ma nota che quello che vuoi è un comportamento che fa questo: vuoi l'isteresi che tira la tensione di soglia nella direzione OPPOSITO di quanto il segnale stia passando. Quindi per un fronte di salita si desidera che la soglia sia leggermente superiore al punto zero e quindi quando lo stato cambia la soglia viene portata a un livello inferiore.


1
+1 per la descrizione aggiuntiva di come dovrebbe funzionare la direzione dell'isteresi. Il paragrafo 2 è un buon consiglio, ma anche farlo internamente va bene, a condizione che sia fatto bene, cosa che in questo esempio non sembra essere il caso.
Trevor_G,

@Trevor_G -: ^)
Michael Karas,

1
@Ipomania - So che puoi leggere il singolo timer nell'ISR. Ma a meno che il timer non abbia un doppio buffer in modo che un registro di uscita mantenga il conteggio da un trigger mentre il timer stesso può continuare a contare, diventa necessario arrestare il timer in modo da poterlo leggere e quindi riattivarlo dopo che è stato letto . Molti timer MCU non hanno un doppio buffer in questo modo e quindi il tempo di elaborazione per entrare nell'ISR a quando il timer viene riabilitato viene perso il tempo sulla misurazione del periodo di tempo per il prossimo mezzo ciclo. Dipende in una certa misura dalla velocità con cui il timer viene cronometrato (continua)
Michael Karas,

1
(continua dall'alto) ma non vuoi mai essere nella situazione in cui stai leggendo un valore di conteggio quando un orologio potrebbe arrivare contemporaneamente per modificare il conteggio. Non ho studiato l'MCU specifico che stai utilizzando per vedere se il tuo timer ha un doppio buffer su un evento di acquisizione trigger o meno.
Michael Karas,

1
@Ipomania - Per un capriccio ho guardato il tuo foglio dati AVR MCU collegato e ho visto che la funzione di acquisizione dell'ingresso del timer ha un doppio buffer !! È un dato di fatto che il timer da queste parti sembra abbastanza robusto. Sono passati quasi 15 anni da quando ho usato qualsiasi parte AVR.
Michael Karas,

6

Il problema con questo scenario è che c'è un ritardo tra la commutazione del comparatore e l'interruzione che viene gestita al punto in cui si cambia il pin "isteresi".

La tua banda di isteresi è anche piuttosto piccola per quel livello di segnale considerando quello per cui la stai usando. Soprattutto quando vedo quanto rumore c'è su quell'onda quadra sul tuo ambito.

Tenendo conto di entrambi questi fattori, esiste un'alta probabilità che a determinati livelli di input si ottengano più fronti dal comparatore prima di poter gestire il primo. Controllare per vedere qual è lo stato del comparatore durante quel gestore di interrupt non sarà di grande aiuto poiché può trovarsi in entrambi gli stati.

Sfortunatamente non hai spiegato in dettaglio come funziona il gestore.

Il gestore dovrebbe comunque lavorare in questo modo.

  1. Quando il valore di isteresi nello stato di soglia alta dovresti attendere un interruzione di fronte negativa.

  2. Quando arriva detto interruzione di fronte negativo, imposta l'isteresi sul valore basso, attendi alcuni cicli, quindi cancella qualsiasi interruzione in sospeso e inizia ad aspettare un'interruzione di fronte positiva.

  3. Quando arriva detto interruzione di fronte positivo, riportare il perno di isteresi al valore alto, attendere alcuni cicli, cancellare qualsiasi interruzione in sospeso e ricominciare ad aspettare di nuovo un'interruzione di fronte negativa.

  4. Ripetere dal passaggio 1.

A proposito, non mi piace troppo il modo in cui stai usando il riferimento del comparatore come bias per il segnale. Ciò si traduce in un piccolo dialogo incrociato sia dal segnale al riferimento sia dall'isteresi al segnale, soprattutto con segnali a bassa frequenza. Concesso con quei valori che l'effetto dovrebbe essere piccolo, ma per purezza, una distorsione separata sul segnale sarebbe migliore.

EDIT: Re il tuo codice.

Nell'istruzione else si modifica il fronte di interruzione prima di impostare l'isteresi.

In nessun caso, si mette in pausa e si cancella qualsiasi interruzione in sospeso prima di tornare. (Nota, la modifica del registro di controllo degli interrupt può creare interruzioni da solo.)

Non so se l'Atmega rientra nuovamente negli interrupt, ovvero se un fronte successivo interromperà il gestore ancora in esecuzione dal bordo precedente. In tal caso, è necessario gestire la concorrenza in modo appropriato.

Non sono sicuro a cosa serva la parte PORTC, ma probabilmente deve passare alla parte qualificata.


Grazie mille, domani proverò i tuoi suggerimenti e ti darò un aggiornamento. Per quanto riguarda il mio ISR, ho un'istruzione if-else if per lo scenario esatto che hai descritto escludendo l'attesa.
Shibalicious,

1
@Ipomania dovresti modificare la tua domanda e pubblicare il tuo codice gestore interrupt in modo che la gente possa vedere se stai sbagliando da qualche parte. Tuttavia, potrebbe trattarsi solo di rumore, le tracce dell'oscilloscopio sembrano essere molto più di 50 mV di rumore. Sempre con il rumore, mi aspetto che si corregga da solo, tutto con occasionali impulsi extra alle transizioni.
Trevor_G,

Questo è quello che mi aspettavo anche io. Lo farà il prima possibile.
Shibalicious,

1
@Hypomania vedi modifica
Trevor_G

1
@Ipomania Perché potresti ottenere un altro interrupt tra questi due comandi. Ho anche modificato la modifica ...
Trevor_G,

3

Questo effetto è simile al rimbalzo del contatto e può essere mitigato dalle stesse tecniche di rimbalzo che utilizzeresti per i pulsanti.

  • Decidi il tempo di rimbalzo Td
  • mantenere il timestamp dell'ultimo interrupt di bordo in una variabile
  • se l'intervallo tra l'interruzione corrente e l'ultima è inferiore a Td, ignora l'interruzione corrente
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.