Losowe i nieprzewidywalne zachowanie komparatora analogowego

10

Pracuję nad stosunkowo „prostym” projektem, w którym muszę zmierzyć częstotliwość fali sinusoidalnej o różnej amplitudzie i częstotliwości. Dla uproszczenia mam na razie tylko wejście fali sinusoidalnej o stałej częstotliwości (27 Hz) (ujemne wejście komparatora), które można zmieniać tylko amplitudą (za pomocą potencjometru). Dodatnie wejście komparatora jest ustawione na Vcc / 2. Wyjście komparatora jest następnie podawane do wejściowego rejestru przechwytywania mikrokontrolera atmega2560 w celu pomiaru częstotliwości.

Problem polega na tym, że przy pewnych amplitudach sygnału wejściowego mam dość intensywne przełączanie (lub czasami martwe pasma) na wyjściu, które wygląda następująco:

wprowadź opis zdjęcia tutaj

Gdzie jako oczekiwany wynik powinien wyglądać mniej więcej tak:

wprowadź opis zdjęcia tutaj

Rzeczy, których do tej pory próbowałem:

Korzystanie z wewnętrznego komparatora atmega2560. Korzystanie z zewnętrznego komparatora. Wprowadzenie histerezy za pomocą oprogramowania i obwodu wyzwalającego Schmitta. Próbowałem różnych ustawień wejściowych, w tym stałej konfiguracji odniesienia i konfiguracji fragmentatora danych. Próbuję różnych atmega2560. Próbowanie różnych prędkości zegara.

Niektóre rozwiązania były bardziej stabilne niż inne, ale żadne z nich nie było prawie do przyjęcia. Osiągnąłem dotychczas najbardziej stabilną konfigurację:

wprowadź opis zdjęcia tutaj

Dzięki tej konfiguracji pewne rzeczy poprawiają / zmieniają stabilność, jednak wciąż nie są prawie idealne:

Zmiana wartości R5 w celu zwiększenia histerezy. Całkowite usunięcie C2 (nie wiem dlaczego). Dotykanie drutów na płycie chleba (całkiem sporo obok siebie). Przełączanie zasilaczy z zewnętrznego na USB i odwrotnie.

W tym momencie albo szum, mój przetwornik, z którym generuję falę sinusoidalną, albo robię coś bardzo fundamentalnie niepoprawnie. Ten obwód działał dla innych osób bez żadnych problemów, więc coś musi być nie tak z moją konfiguracją lub środowiskiem.

Jeśli ktoś ma jakieś sugestie, byłbym bardzo wdzięczny za twój czas.

Oto moje minimalne źródło:

#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);
    }
}

Oto link do schematu obwodu i samej biblioteki:

http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/

AKTUALIZACJA:

Wypróbowałem wszystkie twoje sugestie, żadna z nich nie działała, tylko jedna. Usunięcie flag przerwań lub wyłączenie przerwań wewnątrz lub na zewnątrz ISR tak naprawdę nie miało żadnego efektu. Wydaje mi się, że źle rozumiem, jak naprawdę działa rejestr komparatora układu.

Jak wspomniałem na początku, zamierzałem użyć przechwytywania wejściowego do pomiaru częstotliwości fali prostokątnej pochodzącej z fali sinusoidalnej. Wyjście komparatora jest podawane do wejściowego styku przechwytującego, a następnie użyj timerów do pomiaru okresu, proste.

Oto schemat komparatora analogowego atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , strona 265:

wprowadź opis zdjęcia tutaj

Jak widać, komparator ma dwa wyjścia: ACO i ACIS0 + ACIS1. ACO jest ustawiane, gdy + wejście> - wejście, kasowane, gdy + wejście <- wejście. ACIS0 + ACIS1 są bitami wyboru krawędzi.

Na początku robiłem sprawdzanie typu krawędzi w moim ISR. Zamiast tego zmieniłem ISR na to:

    ISR(ANALOG_COMP_vect) {

     if (!(ACSR &  (1<<ACO))) { // + < -
         /* Set PIND5 to 0V */
         PORTD &= ~(1 << PIND5);
    }
    else  {
       /* Set PIND5 to 5V */
       PORTD |= (1 << PIND5);
    }
}

Wyjście zachowywało się bezbłędnie (tak jak na drugim zdjęciu). Następnie przystąpiłem do pomiaru szerokości impulsów, ale wyniki nie były świetne. Intensywne przełączanie na moim wyświetlaczu LCD, liczby przeskakują do wartości losowych lub pozostają na poziomie 0, pomimo posiadania czystego sygnału. Przepisałem swój kod wiele razy, używając różnych warunków, jedynym półstabilnym rozwiązaniem, jakie do tej pory mam, jest:

#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);
}

Mam na myśli, że przez półstabilny mam prawidłową wartość 1/3 razy. Innym razem 2/3 razy jest to połowa poprawnej wartości lub wartość losowa. Próbowałem użyć bitów rejestru timera do instrukcji warunkowych, a także bitów rejestru komparatora w moim ISR, jest to jedyna konfiguracja, która działa.

Później tego samego dnia użyłem zewnętrznego komparatora z identyczną konfiguracją i źródłem (wyłączając wszystkie linie związane z komparatorem). Jego sygnał wyjściowy został wprowadzony do wejścia przechwytującego i działał zgodnie z przeznaczeniem (nawet nie potrzebował żadnej histerezy).

W tym momencie mogę powiedzieć, że rozwiązałem to za pomocą zewnętrznego komparatora, ale nie mam pojęcia, dlaczego wewnętrzny nie zachowuje się sam. Przeczytałem wiele postów i przewodników na ten temat, czytałem różne biblioteki, próbowałem je naśladować bez zadowalającego rezultatu. Arkusz danych ma tylko 5 stron na całej jednostce porównawczej, przeczytałem go wiele razy i nie widzę, co robię źle.

Chciałbym dowiedzieć się, jak prawidłowo z niego korzystać, ale jeśli to się nie powiedzie, mam kopię zapasową. Jeśli masz jakieś dodatkowe informacje, jest to bardzo mile widziane.

Shibalicious
źródło
4
dla rozruszników ... dodaj rezystor 1M między wyjściem a wejściem + ve. To właśnie tworzy histerezę, a nie twój R5 ... który po prostu zmienia odniesienie
JonRB
1
W jaki sposób można uzyskać obrazy zakresu wyjścia z komparatora, który znajduje się w układzie scalonym i jest niedostępny?
Andy aka
2
Czy wyłączasz kolejne przerwania po wejściu do ISR? Być może trzeba - być może większość ISR otrzymuje podwójne trafienia.
Andy aka
1
W jaki sposób przełączasz pin histerezy i czy kwalifikujesz go według bieżącej wartości. Opóźnienie między przerwaniem a przełączeniem może się z Tobą niepokoić.
Trevor_G
1
nie pokazano na schemacie wewnętrznej pojemności między pin5 a pin6, czy możesz użyć wewnętrznego podciągnięcia na pin7, aby zamiast tego zrobić histerizę?
Jasen

Odpowiedzi:

13

Przeczytałem, że używasz przetwornika cyfrowo-analogowego do generowania sygnału fali sinusoidalnej. Wyjścia przetwornika cyfrowo-analogowego mogą powodować usterki przy zmianach stanu wyjściowego, dlatego zdecydowanie należy zastosować filtr analogowy do wyjścia przetwornika cyfrowo-analogowego przed wprowadzeniem go do obwodu komparatora. Może to pomóc w zapobieganiu niektórym wyzwalaczom podwójnego przerwania, które mogą wystąpić.

Chciałbym również skomentować, że naprawdę chcesz używać zewnętrznego komparatora do tego rodzaju problemów, abyś mógł zastosować histerezę z rezystorami bez użycia oprogramowania. Umożliwi to także lepszą izolację problemów, ponieważ można bezpośrednio monitorować wyjście komparatora.

Ostatni komentarz dotyczy rodzaju używanej histerezy. Trochę trudno jest zobaczyć dokładnie, jakiego schematu używasz, ale zauważ, że to, czego chcesz, to zachowanie, które to robi: Chcesz histerezy, która pociąga napięcie progowe w kierunku przeciwnej, niż sygnał przechodzi. Tak więc dla zbocza narastającego chcesz, aby próg był nieco wyższy niż punkt zerowy, a następnie, gdy stan się zmienia, próg zostaje podniesiony na niższy poziom.

Michael Karas
źródło
1
+1 za dodatkowy opis tego, jak powinien działać kierunek histerezy. Ustęp 2 jest dobrą radą, ale robienie tego wewnętrznie jest również w porządku, pod warunkiem, że jest to zrobione dobrze, co w tym przykładzie wydaje się nie mieć miejsca.
Trevor_G
@Trevor_G -: ^)
Michael Karas
1
@ Hipomania - Wiem, że możesz przeczytać pojedynczy licznik czasu w ISR. Ale jeśli licznik czasu nie zostanie podwójnie zbuforowany, tak aby rejestr wyjściowy przechowywał licznik od wyzwalacza, podczas gdy sam licznik może kontynuować odliczanie, konieczne jest zatrzymanie licznika, aby można go było odczytać, a następnie włączyć ponownie po odczytaniu . Wiele timerów MCU nie jest podwójnie buforowanych w ten sposób, a zatem czas przetwarzania, aby dostać się do ISR do momentu ponownego włączenia timera, jest tracony na pomiarze czasu okresu dla następnego półcyklu. Zależy to w pewnym stopniu od tego, jak szybko zegar jest taktowany (ciąg dalszy)
Michael Karas
1
(ciąg dalszy od góry), ale nigdy nie chcesz być w sytuacji, gdy czytasz wartość zliczania, kiedy zegar może nadejść w tym samym czasie, aby zmienić liczbę. Nie badałem konkretnego MCU, którego używasz, aby sprawdzić, czy twój zegar jest podwójnie buforowany w przypadku zdarzenia przechwytywania wyzwalacza, czy nie.
Michael Karas
1
@ Hipomania - kapryśny spojrzałem na twoją połączoną kartę danych AVR MCU i zobaczyłem, że funkcja przechwytywania wejścia timera jest podwójnie buforowana !! W rzeczywistości zegar w tych częściach wygląda dość solidnie. Minęło prawie 15 lat, odkąd użyłem jakichkolwiek części AVR.
Michael Karas
6

Problemami w tym scenariuszu jest opóźnienie czasowe między przełączeniem komparatora a obsłużeniem przerwania do momentu przełączenia pinu „histerezy”.

Twoje pasmo histerezy jest również dość małe dla tego poziomu sygnału, biorąc pod uwagę, do czego go używasz. Zwłaszcza gdy widzę, ile szumu na tej kwadratowej fali w twoim lunecie.

Biorąc pod uwagę oba te czynniki, istnieje duże prawdopodobieństwo, że przy pewnych poziomach wejściowych uzyskasz wiele zboczy z komparatora, zanim będziesz w stanie obsłużyć pierwszy. Sprawdzanie, czy stan komparatora jest podczas tej procedury obsługi przerwań, niewiele pomoże, ponieważ może być w dowolnym stanie.

Niestety w pytaniu nie opisano szczegółowo, jak działa moduł obsługi.

Twój przewodnik powinien jednak działać w ten sposób.

  1. Gdy wartość histerezy w stanie wysokiego progu powinieneś poczekać na ujemne przerwanie zbocza.

  2. Kiedy pojawi się wspomniane przerwanie ujemnego zbocza, przełącz histerezę na niską wartość, odczekaj kilka cykli, a następnie usuń wszelkie oczekujące przerwanie zbocza i zacznij czekać na dodatnie przerwanie zbocza.

  3. Kiedy pojawi się wspomniane przerwanie dodatniego zbocza, przełącz ponownie pin histerezy na wysoką wartość, odczekaj kilka cykli, usuń wszelkie oczekujące przerwanie i zacznij ponownie czekać na ujemne przerwanie zbocza.

  4. Powtórz od kroku 1.

BTW Nie przepadam za sposobem, w jaki używasz referencji komparatora jako polaryzacji sygnału. Powoduje to niewielkie przesłuchy zarówno od sygnału do odniesienia, jak i od histerezy do sygnału, szczególnie w przypadku sygnałów o niskiej częstotliwości. Przyznane z tymi wartościami, które wpływ powinny być małe, ale dla czystości lepsze byłoby osobne odchylenie sygnału.

EDYCJA: Ponownie kod.

W instrukcji else zmieniasz krawędź przerwania przed ustawieniem histerezy.

W żadnym wypadku nie wstrzymujesz i nie usuwasz żadnych oczekujących przerwań przed powrotem. (Uwaga: zmiana rejestru sterującego przerwaniami może sama tworzyć przerwania.)

Nie wiem, czy Atmega ponownie przerywa przerwanie, to znaczy, czy kolejna krawędź przerwie wciąż działający program obsługi od poprzedniej krawędzi. Jeśli tak, musisz odpowiednio obsługiwać współbieżność.

Nie jestem pewien, do czego służy część PORTC, ale prawdopodobnie musi przejść do części kwalifikowanej.

Trevor_G
źródło
Dziękuję bardzo, jutro wypróbuję wasze sugestie i dam wam aktualizację. Jeśli chodzi o mój ISR, mam instrukcję if-else if dla dokładnie opisanego scenariusza z wyłączeniem oczekiwania.
Shibalicious
1
@ Hipomania powinieneś edytować swoje pytanie i opublikować swój kod obsługi przerwań, aby ludzie mogli zobaczyć, czy gdzieś się bałaganisz. Może to być po prostu szum, ślady lunety wyglądają, jakby było tam ponad 50mV szumu. Nadal z szumem oczekiwałbym, że to się poprawi, wszystko to przy sporadycznych dodatkowych impulsach na przejściach.
Trevor_G
Tego też się spodziewałem. Zrobi to jak najszybciej.
Shibalicious
1
@Hypomania patrz edycja
Trevor_G
1
@Hypomania Ponieważ można uzyskać kolejną przerwę między tymi dwoma poleceniami. Edytowałem również edycję ...
Trevor_G
3

Ten efekt jest podobny do odskakiwania przez kontakt i można go złagodzić za pomocą tych samych technik odbijania, których używasz dla przycisków.

  • Zdecyduj o czasie odbicia Td
  • zachowaj znacznik czasu ostatniego przerwania zbocza w zmiennej
  • jeśli czas między bieżącym przerwaniem a ostatnim jest krótszy niż Td, zignoruj ​​bieżące przerwanie
Dmitrij Grigoriew
źródło