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

10

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

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

Shibalicious
источник
4
для начала ... добавьте резистор 1М между выходом и входом + ve. Это то, что создает гистерезис, а не ваш R5 ... это просто меняет ссылку
JonRB
1
Как вы можете получить объемные изображения на выходе компаратора, который находится внутри чипа и недоступен?
Энди ака
2
Вы отключаете дальнейшие прерывания при входе в ISR? Вам может понадобиться - может быть, большинство ISR получают двойные удары.
Энди ака
1
Как вы переключаете вывод гистерезиса и квалифицируете ли вы его по текущему значению? Задержка между прерыванием и переключателем может быть связана с вами.
Trevor_G
1
На схеме не показана внутренняя емкость между контактами 5 и 6, можете ли вы вместо этого использовать внутренний подтягивающий контакт на контакте 7, чтобы вызвать истерику?
Jasen

Ответы:

13

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

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

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

Майкл Карас
источник
1
+1 за дополнительное описание того, как должно работать направление гистерезиса. Параграф 2 - хороший совет, но делать это внутренне тоже хорошо, при условии, что все сделано правильно, что в данном примере, похоже, не так.
Trevor_G
@Trevor_G -: ^)
Майкл Карас
1
@ Hypomania - я знаю, что вы можете прочитать один таймер в ISR. Но если таймер не имеет двойной буферизации, так что выходной регистр хранит счет от триггера, а сам таймер может продолжать отсчет, тогда возникает необходимость остановить таймер, чтобы вы могли прочитать его, а затем снова включить его после того, как он был прочитан , Многие таймеры MCU не имеют двойной буферизации, подобной этой, и, таким образом, время обработки, чтобы попасть в ISR при повторном включении таймера, теряется во время измерения периода для следующей половины цикла. В какой-то степени это зависит от того, как быстро синхронизируется таймер (продолжение)
Майкл Карас,
1
(продолжение сверху), но вы никогда не захотите оказаться в ситуации, когда вы читаете значение счетчика, когда одновременно могут приходить часы для изменения счетчика. Я не исследовал конкретный MCU, который вы используете, чтобы увидеть, является ли ваш таймер двойной буферизацией при событии захвата триггера или нет.
Майкл Карас,
1
@ Hypomania - по прихоти я посмотрел на ваш связанный лист данных AVR MCU и увидел, что функция захвата входа таймера с двойной буферизацией! На самом деле таймер в этих местах выглядит довольно надежно. Прошло почти 15 лет с тех пор, как я использовал какие-либо детали AVR.
Майкл Карас,
6

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

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

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

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

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

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

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

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

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

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

РЕДАКТИРОВАТЬ: Re ваш код.

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

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

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

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

Trevor_G
источник
Большое спасибо, завтра я попробую ваши предложения и предоставлю вам обновление. Что касается моего ISR, у меня есть оператор if-else if для точного сценария, который вы описали, исключая ожидание.
Shibalicious
1
@ Hypomania, вы должны отредактировать свой вопрос и опубликовать свой код обработчика прерываний, чтобы люди могли видеть, если вы что-то напутали. Хотя это может быть просто шум, следы вашего прицела выглядят так, как будто там более 50 мВ шума. Все еще с шумом, я ожидал бы, что это исправит себя, все с случайными дополнительными импульсами в переходах.
Trevor_G
Я тоже этого ожидал. Сделаем это как можно скорее.
Shibalicious
1
@ Hypomania см. Редактировать
Trevor_G
1
@ Hypomania Потому что вы можете получить еще одно прерывание между этими двумя командами. Я также отредактировал редактирование ...
Trevor_G
3

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

  • Определите время отката Td
  • сохранить метку времени последнего краевого прерывания в переменной
  • если время между текущим прерыванием и последним меньше Td, игнорирует текущее прерывание
Дмитрий Григорьев
источник