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:
Gdzie jako oczekiwany wynik powinien wyglądać mniej więcej tak:
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ę:
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:
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.
źródło
Odpowiedzi:
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.
źródło
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.
Gdy wartość histerezy w stanie wysokiego progu powinieneś poczekać na ujemne przerwanie zbocza.
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.
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.
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.
źródło
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.
Td
Td
, zignoruj bieżące przerwanieźródło