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.