Випадкова та непередбачувана поведінка аналогового компаратора


10

Я працюю над відносно "простим" проектом, де мені потрібно виміряти частоту синусоїди, яка змінюється за амплітудою та частотою. Для спрощення речей, на даний момент у мене є лише синусоїдальний вхід фіксованої частоти (27 ГГц) (негативний вхід компаратора), який можна змінювати лише за амплітудою (використовуючи потенціометр). Позитивний вхід компаратора встановлений на Vcc / 2. Потім вихід компаратора подається в регістр захоплення входу мікроконтролера atmega2560 для вимірювання частоти.

Проблема полягає в тому, що при певних амплітудах вхідного сигналу я отримую досить інтенсивне переміщення (або іноді мертвих смуг) на виході, який виглядає приблизно так:

введіть тут опис зображення

Де очікуваний результат повинен виглядати приблизно так:

введіть тут опис зображення

Що я спробував досі:

Використання внутрішнього компаратора atmega2560. Використання зовнішнього компаратора. Представляємо гістерезис за допомогою програмного забезпечення та тригерної схеми Шмітта. Випробували різні параметри введення, включаючи фіксовану довідкову настройку та налаштування зрізу даних. Спробуйте різні atmega2560's. Спробуйте різні тактові швидкості.

Деякі рішення були більш стійкими, ніж інші, але жодне з них не було десь прийнятним. Я вирішив найбільш стабільну конфігурацію до цих пір:

введіть тут опис зображення

За допомогою цього налаштування деякі речі покращують / змінюють стабільність, однак все ще ніде не ідеально:

Зміна значення R5 для збільшення гістерезису. Видалення С2 повністю (навіть не знаю чому). Торкання проводів на дошці (їх досить багато поруч). Перемикання джерел живлення із зовнішнього на 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/

ОНОВЛЕННЯ:

Я спробував усі ваші пропозиції, жодна з них не працювала, окрім однієї. Видалення прапорів переривання або відключення перерв всередині або поза МСУ насправді не мало жодного ефекту. Мені здається, неправильно розумію, як насправді працює реєстр компараторів мікросхем.

Як я вже згадував спочатку, я збирався використовувати вхідне захоплення для вимірювання частоти квадратної хвилі, отриманої від синусоїди. Вихід компаратора подається на вхідний штифт захоплення, потім використовуються таймери для вимірювання періоду, прості.

Ось аналогова схема порівняння 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. ACIS0 + ACIS1 - це біт вибору краю.

Я те, що я робив спочатку, перевіряв тип ребра в моєму рейтингу. Натомість я змінив 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 сторінок у всьому блоці порівняння, я перечитував його багато разів, і я не бачу, що я роблю неправильно.

Я хотів би дізнатися, як правильно його використовувати, але якщо це не вдається, у мене є резервна копія. Якщо у вас є додаткові дані, це дуже вдячно.


4
для початку ... додайте 1M резистор між виходом і входом +. Це те, що створює гістерезис, а не ваш R5 ... що просто змінює посилання
JonRB

1
Як ви можете створити масштабні зображення виводу з компаратора, який знаходиться всередині мікросхеми і недоступний?
Енді ака

2
Ви відключаєте подальші перерви під час введення ISR? Можливо, вам знадобиться - можливо, більшість ISR отримують подвійні звернення.
Енді ака

1
Як ви перемикаєте шпильку гістерезису і чи кваліфікуєте ви її за поточним значенням. Затримка між переривкою і перемикачем може бути прикручена до вас.
Trevor_G

1
не показано на вашій схемі - це внутрішня ємність між pin5 і pin6, чи можете ви використовувати внутрішній підтягувач на pin7, щоб зробити ваш істерис замість цього?
Ясен

Відповіді:


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.

До речі, я не дуже захоплююсь тим, як ви використовуєте посилання компаратора як ухил для сигналу. Це призводить до невеликої перехресної розмови як від сигналу до опорного, так і від гістерезису до сигналу, особливо з низькочастотними сигналами. З урахуванням тих значень, ефект яких повинен бути невеликим, але для чистоти краще буде окремий зміщення сигналу.

РЕДАКТУВАННЯ: Перевірте свій код.

В операторі else ви змінюєте край переривання перед тим, як встановити гістерезис.

Ні в якому разі не робити паузи та очищати будь-які очікувані перерви перед поверненням. (Зауважте, зміна реєстру контролю переривань може створювати переривання самостійно.)

Я не знаю, чи дійсно Atmega перериває переривання, тобто якщо наступний край перерве ще запущений обробник з попереднього краю. Якщо це так, вам потрібно поводитися з одночасністю.

Не впевнений, для чого призначена частина PORTC, але, ймовірно, потрібно перейти до кваліфікованої частини.


Дуже дякую, я спробую ваші пропозиції завтра і дам вам оновлення. Що стосується мого ISR, у мене є if-else, якщо вказано точний сценарій, який ви описали, виключаючи очікування.
Shibalicious

1
@Hypomania, ви повинні відредагувати своє запитання та опублікувати код обробника переривань, щоб люди могли побачити, чи ви десь плутаєтесь. Це може бути просто шум, але ваш обсяг слідів виглядає так, що там є шум понад 50мВ. І все-таки з шумом я б очікував, що він виправить себе, будь то з випадковими зайвими імпульсами при переходах.
Trevor_G

Це я і очікував. Зробимо це якнайшвидше.
Shibalicious

1
@Hypomania див. Редагувати
Trevor_G

1
@Hypomania Тому що ви могли отримати ще одне переривання між цими двома командами. Я також редагував правки ...
Trevor_G

3

Цей ефект схожий на контактний відмов, і його можна пом'якшити тими самими методами дебютації, які ви використовували б для кнопок.

  • Визначте час дебютування Td
  • зберігати часову позначку останнього краю переривання в змінній
  • якщо час між поточним перериванням і останнім менше Td, ігноруйте поточне переривання
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.