Я работаю над относительно «простым» проектом, в котором мне нужно измерить частоту синусоидальной волны, которая варьируется по амплитуде и частоте. Для упрощения, на данный момент у меня есть только синусоидальный вход с фиксированной частотой (27 Гц) (отрицательный вход компаратора), который можно изменять только по амплитуде (используя потенциометр). Положительный вход компаратора установлен на Vcc / 2. Затем выход компаратора подается на входной регистр захвата микроконтроллера atmega2560 для измерения частоты.
Проблема в том, что при определенных амплитудах входного сигнала я получаю довольно интенсивное переключение (или иногда мертвые зоны) на выходе, который выглядит так:
Где ожидаемый результат должен выглядеть примерно так:
Вещи, которые я пробовал до сих пор:
Использование внутреннего компаратора atmega2560. Использование внешнего компаратора. Введение гистерезиса с использованием программного обеспечения и схемы триггера Шмитта. Пробовал различные входные настройки, включая фиксированную настройку эталона и настройку среза данных. Пробую разные atmega2560. Пробую разные тактовые частоты.
Некоторые решения были более стабильными, чем другие, но ни одно из них не было приемлемым. Я остановился на самой стабильной конфигурации:
При такой настройке некоторые вещи улучшают / изменяют стабильность, но все еще не достигли совершенства:
Изменение значения R5 для увеличения гистерезиса. Полное удаление C2 (не знаю почему). Касаясь проводов на макете (их довольно много рядом). Переключение источников питания с внешнего на USB и наоборот.
На данный момент это либо шум, мой ЦАП, с помощью которого я генерирую синусоидальную волну, либо я делаю что-то очень фундаментальное неправильно. Эта схема работала для других людей без каких-либо проблем, поэтому что-то должно быть не так с моей конфигурацией или средой.
Если у кого-то есть предложения, я буду очень признателен за ваше время.
Вот мой минимальный источник:
#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);
}
}
Кроме того, вот ссылка на принципиальную схему и саму библиотеку:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
ОБНОВИТЬ:
Я испробовал все ваши предложения, ни одно из них не сработало, кроме одного. Очистка флагов прерываний или отключение прерываний внутри или за пределами ISR на самом деле не имели никакого эффекта. Кажется, я неправильно понимаю, как на самом деле работает регистр компаратора чипа.
Как я упоминал вначале, я собирался использовать входной захват для измерения частоты прямоугольной волны, полученной из синусоидальной волны. Выход компаратора подается на входной вывод захвата, затем с помощью таймеров для измерения периода просто.
Вот схема аналогового сравнения atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , стр. 265:
Как видите, компаратор имеет два выхода, ACO и ACIS0 + ACIS1. ACO устанавливается, когда + input> - input, очищается, когда + input <- input. ACIS0 + ACIS1 - биты выбора края.
Первоначально я проверял тип ребра в моем ISR. Вместо этого я изменил ISR:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
И вывод себя вел себя без нареканий (как на второй картинке). Затем я продолжил измерять ширину импульсов, но результаты были невелики. Интенсивное переключение на моем ЖК-дисплее, числа переходят в случайные значения или остаются на 0, несмотря на чистый сигнал. Я переписывал свой код много раз, используя разные условия, единственное полуустойчивое решение, которое я получил до сих пор, это:
#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);
}
Под полустабильным, я имею в виду, я получаю правильное значение в 1/3 раза. В других случаях в 2/3 случаев это либо половина правильного значения, либо случайное значение. Я попытался использовать регистровые биты таймера для условных операторов, а также регистровые биты компаратора в моем ISR, это единственная конфигурация, которая работает.
Позже я использовал внешний компаратор с идентичной настройкой и источником (исключая все строки, относящиеся к компаратору). Его выход был подан на входной фиксатор, и он работал как положено (даже не нуждался в гистерезисе).
На данный момент я могу сказать, что я получил решение с помощью внешнего компаратора, однако я понятия не имею, почему внутренний не ведет себя сам. Я прочитал много постов и руководств по этому вопросу, прочитал различные библиотеки, пытался подражать им без какого-либо приемлемого результата. Таблица данных содержит всего 5 страниц на весь блок компаратора, я перечитываю его много раз и не вижу, что делаю неправильно.
Я хотел бы узнать, как правильно его использовать, но в случае неудачи у меня есть резервная копия. Если у вас есть какие-либо дополнительные материалы, это очень ценится.