Случайное и непредсказуемое поведение аналогового компаратора

Я работаю над относительно «простым» проектом, в котором мне нужно измерить частоту синусоидальной волны, которая варьируется по амплитуде и частоте. Чтобы упростить ситуацию, на данный момент у меня есть только синусоидальный сигнал фиксированной частоты (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 страниц на весь блок компараторов, много раз перечитывал и не вижу, что я делаю не так.

Я хотел бы узнать, как правильно его использовать, но если это не удастся, у меня есть резервная копия. Если у вас есть какие-либо дополнительные материалы, мы будем очень признательны.

для начала... добавьте резистор 1M между выходом и входом +ve. Это то, что создает гистерезис, а не ваш R5 ... который просто меняет ссылку
Как вы можете получить масштаб изображения выходного сигнала компаратора, который находится внутри чипа и недоступен?
@JonRB, извините, я забыл упомянуть, контакт 5 переключается между Vcc и 0, что сдвигает опорное значение +/- 25 мВ, что, по сути, и делает триггер Шмитта.
@Энди, также известный как Энди, я переключаю контакт с аналоговых компараторов, прерываю сервисную процедуру / опрос
Вы переключаетесь дважды в течение 1 ISR? Мне кажется, что изображение 1 может указывать на проблему с кодом, несмотря на то, что компаратор работает правильно.
@Энди, он же Энди, нет, только один раз за ISR, либо на нарастающем, либо на спадающем фронте.
Вы отключаете дальнейшие прерывания при входе в ISR? Возможно, вам понадобится - может быть, большинство ISR получают двойные попадания.
@ Энди, он же, я этого не пробовал, нет. Это имеет смысл, однако библиотека I, созданная для этой схемы, имеет смысл. Хотя попробую завтра. Чего я не могу понять, так это почему каждые ~ 0,3 VI получают переключение или мертвые зоны, тогда как другие входные амплитуды в порядке?
Последняя часть вашего комментария не имеет смысла - откуда взялись 0,3 вольта? Попробуйте прерывание, чтобы увидеть, улучшится ли оно.
@ Энди, он же, например, изменяющаяся амплитуда синусоидальной волны. Например, переход от 4 В пик-пик к 3,7 В пик-пик. 0,3v — очень грубое приближение, но я пытаюсь сказать, что случайное поведение не является непрерывным по входной амплитуде. А ISR обязательно попробую, спасибо.
Как вы переключаете штифт гистерезиса и определяете ли вы его по текущему значению. Задержка между прерыванием и переключением может сбить вас с толку.
@Trevor_G, компаратор запускает прерывание, я устанавливаю контакт 5 либо на высокий, либо на низкий уровень в зависимости от фронта. Вот и все. Вы можете посмотреть «Freqmeasure.h» на github, у меня точно такой же ISR, за исключением всего кода таймеров.
@Hypomania, к сожалению, я недостаточно знаком с микро, чтобы знать, как узнать, какое у него преимущество.
на вашей схеме не показана внутренняя емкость между контактами 5 и 6, можете ли вы вместо этого использовать внутреннюю подтяжку на контакте 7, чтобы создать гистерезис?
@ Джейсен, я, конечно, могу, да, какая разница?
@Andyaka обновлено
Использование ACO напрямую улучшит внешний вид прямоугольной волны, поскольку, по-видимому, она лучше исправится. Однако нет гарантии, что он не подпрыгивает, пока вы смотрите на него в коде. Было бы интересно увидеть новую трассировку прицела, действительно приближенную к переходу.

Ответы (4)

Я читал, что вы используете ЦАП для генерации синусоидального сигнала. Выходы ЦАП могут глючить при изменении состояния выхода, поэтому вам обязательно следует применить некоторую аналоговую фильтрацию к выходу ЦАП, прежде чем подавать его в схему компаратора. Это может помочь предотвратить некоторые триггеры двойного прерывания, которые могут возникнуть.

Я также хотел бы отметить, что вы действительно хотите использовать внешний компаратор для этого типа проблем, чтобы вы могли применять гистерезис с резисторами без использования взаимодействия с программным обеспечением. Это также позволит лучше изолировать проблему, поскольку вы можете напрямую контролировать выходной сигнал компаратора.

Последний комментарий относится к типу используемого вами гистерезиса. Немного сложно понять, какую именно схему вы используете, но обратите внимание, что вам нужно поведение, которое делает это: вам нужен гистерезис, который тянет пороговое напряжение в ПРОТИВОПОЛОЖНОМ направлении, чем сигнал переходит. Таким образом, для нарастающего фронта вы хотите, чтобы порог был немного выше, чем нулевая точка, а затем, когда состояние изменяется, порог перемещается на более низкий уровень.

+1 за дополнительное описание того, как должно работать направление гистерезиса. Пункт 2 — хороший совет, но делать это внутри себя тоже можно, при условии, что все сделано правильно, что в этом примере, похоже, не так.
@Trevor_G - :^)
@Trevor_G, не могли бы вы объяснить, почему это сделано неправильно? Также указан источник.
@MichaelKaras, спасибо за ваши предложения, я попробовал внешний компаратор с резисторами с тем же результатом. Заставляет меня задаться вопросом, шумит ли ЦАП. Источник указан в редакции. Кстати, спасибо за ваш вклад.
@Hypomania ваши следы прицела - все, что вам нужно. Это не работает, как вы ожидаете .. значит что-то не так ;D
@Trevor_G, а, понятно, я вас неправильно понял :) Завтра я все разберу и попробую еще раз, очень ценю ваш вклад.
@MichaelKaras, обновлено
@Trevor_G, обновлено
Всякий раз, когда я измерял время высокого и низкого уровня сигнала, я использовал два таймера, один из которых срабатывал по положительному фронту, а другой — по отрицательному фронту. Это дает целый полупериод для обработки счета от текущего прерывания, в то время как другой таймер начинает накапливать следующий счет. Только с одним таймером вы либо потеряете счетчик времени обработки прерывания, либо получите конфликты между накоплением и чтением таймера одновременно.
@MichaelKaras, но таймер считывается в ISR, что не может быть прервано. Кроме того, он работает без проблем, если я не использую компаратор, но + к идее, проблема в том, что у меня остался только один 16-битный таймер :)
@Hypomania - я знаю, что вы можете прочитать единственный таймер в ISR. Но если таймер не имеет двойной буферизации, так что выходной регистр содержит счетчик от триггера, в то время как сам таймер может продолжать считать, тогда становится необходимым остановить таймер, чтобы вы могли прочитать его, а затем снова включить его после того, как он был прочитан. . Многие таймеры MCU не имеют двойной буферизации, как это, и, таким образом, время обработки для входа в ISR до повторного включения таймера теряется при измерении времени периода для следующего полупериода. Это в некоторой степени зависит от того, как быстро отсчитывается таймер (продолжение)
(продолжение выше), но вы никогда не захотите оказаться в ситуации, когда вы читаете значение счетчика, когда в то же время могут подойти часы, чтобы изменить счет. Я не исследовал конкретный MCU, который вы используете, чтобы увидеть, имеет ли ваш таймер двойную буферизацию в событии захвата триггера или нет.
@Hypomania - По прихоти я посмотрел на ваш связанный лист данных AVR MCU и увидел, что функция захвата ввода таймера имеет двойную буферизацию !! На самом деле таймер в этих частях выглядит вполне солидно. Прошло почти 15 лет с тех пор, как я использовал какие-либо детали AVR.

Проблемы с этим сценарием заключаются в том, что между переключением компаратора и обработкой прерывания существует задержка по времени до момента, когда вы переключаете вывод «гистерезис».

Ваша полоса гистерезиса также довольно мала для этого уровня сигнала, учитывая, для чего вы ее используете. Особенно, когда я вижу, сколько шума на этой прямоугольной волне на вашем прицеле.

Принимая во внимание оба этих фактора, существует высокая вероятность того, что при определенных входных уровнях вы получите несколько фронтов от компаратора, прежде чем сможете обработать первый. Проверка состояния компаратора во время этого обработчика прерывания не поможет, поскольку он может находиться в любом состоянии.

К сожалению, вы не указали в вопросе, как работает обработчик.

Однако ваш обработчик должен работать примерно так.

  1. Когда значение гистерезиса находится в состоянии высокого порога, следует ожидать прерывания по отрицательному фронту.

  2. Когда поступает указанное прерывание по отрицательному фронту, установите гистерезис на низкое значение, подождите несколько циклов, затем сбросьте все ожидающие прерывания и начните ждать прерывания по положительному фронту.

  3. Когда поступит указанное прерывание по положительному фронту, переключите вывод гистерезиса обратно на высокое значение, подождите несколько циклов, очистите все ожидающие прерывания и снова начните ждать прерывания по отрицательному фронту.

  4. Повторите с шага 1.

Кстати, мне не очень нравится, как вы используете ссылку компаратора в качестве смещения для сигнала. Это приводит к небольшим перекрестным помехам как от сигнала к опорному сигналу, так и от гистерезиса к сигналу, особенно с низкочастотными сигналами. Учитывая эти значения, эффект должен быть небольшим, но для чистоты было бы лучше отдельное смещение сигнала.

РЕДАКТИРОВАТЬ: Повторите свой код.

В операторе else вы изменяете фронт прерывания перед установкой гистерезиса.

Ни в том, ни в другом случае вы не останавливаетесь и не сбрасываете все ожидающие прерывания перед возвратом. (Обратите внимание, изменение регистра управления прерываниями может создавать прерывания само по себе.)

Я не знаю, выполняет ли Atmega прерывания с повторным входом, то есть прерывает ли последующий фронт все еще работающий обработчик предыдущего фронта. Если это так, вам нужно правильно обрабатывать параллелизм.

Не уверен, для чего предназначена часть PORTC, но, вероятно, ее нужно переместить в квалифицированную часть.

Большое спасибо, я попробую ваши предложения завтра и дам вам обновление. Что касается моего ISR, у меня есть оператор if-else if для точного сценария, который вы описали, за исключением ожидания.
@Hypomania, вы должны отредактировать свой вопрос и опубликовать код обработчика прерываний, чтобы люди могли видеть, не ошиблись ли вы где-нибудь. Хотя это может быть просто шум, ваши следы прицела выглядят так, как будто там более 50 мВ шума. Тем не менее, с шумом я ожидаю, что он исправится сам по себе, будь то случайные дополнительные импульсы на переходах.
Я тоже этого ожидал. Сделаем это как можно скорее.
Я полностью согласен с замечанием @Trevor_G относительно использования эталонного порога также в качестве эталона для подветренной стороны входного разделительного конденсатора.
@Гипомания см. править
@Trevor_G, мой плохой, PORTC был частью комментария, удалил его. Что касается первой строки вашего редактирования, какое это имеет значение? Это относится только к следующему прерыванию. Ваша вторая строка, никогда не думал, что это может быть проблемой, но я обязательно попробую, спасибо!
@Hypomania Потому что вы можете получить еще одно прерывание между этими двумя командами. Я также редактировал редактирование ...
@Trevor_G, насколько я знаю, все прерывания в AVR блокируются (не прерываются). Кроме того, из таблицы данных: «ACI очищается аппаратно при выполнении соответствующего вектора обработки прерывания. В качестве альтернативы, ACI очищается путем записи логической единицы в флаг». ACI является флагом прерывания аналогового компаратора. Однако прерывания откладываются, что потенциально может вызвать все странное поведение, которое я видел. Завтра попробую отключить глобальные прерывания при входе в ISR, спасибо :)
@ Гипомания, это хорошо. Затем вы должны очистить любой ожидающий флаг в конце подпрограммы, поскольку к тому времени может ожидаться новый. Отключение глобального не требуется.

Этот эффект аналогичен дребезгу контактов, и его можно смягчить с помощью тех же методов устранения дребезга, которые вы использовали бы для кнопок.

  • Определите время устранения дребезга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

Использование «Ответа №» ненадежно. Порядок может меняться в зависимости от нескольких факторов, включая популярность, количество голосов и ряд других факторов. Было бы хорошо, если бы вы ссылались на имена плакатов или вставляли ссылки на конкретные ответы, на которые вы хотите сослаться.
@MichaelKaras, спасибо, действительные баллы. Отредактировано.