Я работаю над относительно «простым» проектом, в котором мне нужно измерить частоту синусоидальной волны, которая варьируется по амплитуде и частоте. Чтобы упростить ситуацию, на данный момент у меня есть только синусоидальный сигнал фиксированной частоты (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 страниц на весь блок компараторов, много раз перечитывал и не вижу, что я делаю не так.
Я хотел бы узнать, как правильно его использовать, но если это не удастся, у меня есть резервная копия. Если у вас есть какие-либо дополнительные материалы, мы будем очень признательны.
Я читал, что вы используете ЦАП для генерации синусоидального сигнала. Выходы ЦАП могут глючить при изменении состояния выхода, поэтому вам обязательно следует применить некоторую аналоговую фильтрацию к выходу ЦАП, прежде чем подавать его в схему компаратора. Это может помочь предотвратить некоторые триггеры двойного прерывания, которые могут возникнуть.
Я также хотел бы отметить, что вы действительно хотите использовать внешний компаратор для этого типа проблем, чтобы вы могли применять гистерезис с резисторами без использования взаимодействия с программным обеспечением. Это также позволит лучше изолировать проблему, поскольку вы можете напрямую контролировать выходной сигнал компаратора.
Последний комментарий относится к типу используемого вами гистерезиса. Немного сложно понять, какую именно схему вы используете, но обратите внимание, что вам нужно поведение, которое делает это: вам нужен гистерезис, который тянет пороговое напряжение в ПРОТИВОПОЛОЖНОМ направлении, чем сигнал переходит. Таким образом, для нарастающего фронта вы хотите, чтобы порог был немного выше, чем нулевая точка, а затем, когда состояние изменяется, порог перемещается на более низкий уровень.
Проблемы с этим сценарием заключаются в том, что между переключением компаратора и обработкой прерывания существует задержка по времени до момента, когда вы переключаете вывод «гистерезис».
Ваша полоса гистерезиса также довольно мала для этого уровня сигнала, учитывая, для чего вы ее используете. Особенно, когда я вижу, сколько шума на этой прямоугольной волне на вашем прицеле.
Принимая во внимание оба этих фактора, существует высокая вероятность того, что при определенных входных уровнях вы получите несколько фронтов от компаратора, прежде чем сможете обработать первый. Проверка состояния компаратора во время этого обработчика прерывания не поможет, поскольку он может находиться в любом состоянии.
К сожалению, вы не указали в вопросе, как работает обработчик.
Однако ваш обработчик должен работать примерно так.
Когда значение гистерезиса находится в состоянии высокого порога, следует ожидать прерывания по отрицательному фронту.
Когда поступает указанное прерывание по отрицательному фронту, установите гистерезис на низкое значение, подождите несколько циклов, затем сбросьте все ожидающие прерывания и начните ждать прерывания по положительному фронту.
Когда поступит указанное прерывание по положительному фронту, переключите вывод гистерезиса обратно на высокое значение, подождите несколько циклов, очистите все ожидающие прерывания и снова начните ждать прерывания по отрицательному фронту.
Повторите с шага 1.
Кстати, мне не очень нравится, как вы используете ссылку компаратора в качестве смещения для сигнала. Это приводит к небольшим перекрестным помехам как от сигнала к опорному сигналу, так и от гистерезиса к сигналу, особенно с низкочастотными сигналами. Учитывая эти значения, эффект должен быть небольшим, но для чистоты было бы лучше отдельное смещение сигнала.
РЕДАКТИРОВАТЬ: Повторите свой код.
В операторе else вы изменяете фронт прерывания перед установкой гистерезиса.
Ни в том, ни в другом случае вы не останавливаетесь и не сбрасываете все ожидающие прерывания перед возвратом. (Обратите внимание, изменение регистра управления прерываниями может создавать прерывания само по себе.)
Я не знаю, выполняет ли Atmega прерывания с повторным входом, то есть прерывает ли последующий фронт все еще работающий обработчик предыдущего фронта. Если это так, вам нужно правильно обрабатывать параллелизм.
Не уверен, для чего предназначена часть PORTC, но, вероятно, ее нужно переместить в квалифицированную часть.
Этот эффект аналогичен дребезгу контактов, и его можно смягчить с помощью тех же методов устранения дребезга, которые вы использовали бы для кнопок.
Td
Td
, игнорировать текущее прерываниеЭто замечание по дизайну от TI точно объясняет проблему зашумленного сигнала и необходимость добавления правильного гистерезиса. Это делается, конечно, с помощью внешнего компаратора, как рекомендует @Michael Karas, а также решает проблему многократного срабатывания при отрицательных переходах входного сигнала, то есть проблему «дребезга контактов», как указано @Trevor_G и @Dmitry Grigoryev в их ответах https:// www.ti.com/lit/ug/tidu020a/tidu020a.pdf?ts=1621988309888&ref_url=https%253A%252F%252Fwww.google.com%252F
пользователь16222
Энди ака
Шибалициус
Шибалициус
Энди ака
Шибалициус
Энди ака
Шибалициус
Энди ака
Шибалициус
Тревор_G
Шибалициус
Тревор_G
Джейсен
Шибалициус
Шибалициус
Тревор_G