Rastgele ve öngörülemeyen analog karşılaştırıcı davranışı


10

Genlik ve frekansta değişen bir sinüs dalgasının frekansını ölçmem gereken nispeten "basit" bir proje üzerinde çalışıyorum. Bir şeyleri basitleştirmek için, şimdilik, sadece bir genlik (bir potansiyometre kullanarak) değiştirilebilen sabit bir frekans (27Hz) sinüs dalgası girişi (karşılaştırıcının negatif girişi) var. Karşılaştırıcının pozitif girişi Vcc / 2 olarak ayarlanmıştır. Daha sonra karşılaştırıcının çıkışı, frekansı ölçmek için atmega2560 mikrodenetleyicinin giriş yakalama kaydına beslenir.

Sorun, giriş sinyalinin belirli genliklerinde, çıkışta şu şekilde görünen oldukça yoğun geçiş (veya bazen ölü bantlar) elde etmemdir:

resim açıklamasını buraya girin

Beklenen çıktı aşağıdaki gibi görünmelidir:

resim açıklamasını buraya girin

Şimdiye kadar denediğim şeyler:

Dahili atmega2560'ın dahili karşılaştırıcısını kullanma. Harici bir karşılaştırıcı kullanma. Yazılım ve Schmitt tetikleme devresi kullanarak histerezis tanıtımı. Sabit referans kurulumu ve veri dilimleyici kurulumu dahil olmak üzere çeşitli giriş kurulumlarını denedi. Farklı atmega2560 deniyor. Farklı saat hızları deniyor.

Bazı çözümler diğerlerinden daha kararlıydı, ancak hiçbiri kabul edilebilir bir yerde değildi. Şimdiye kadarki en istikrarlı yapılandırmaya yerleştim:

resim açıklamasını buraya girin

Bu kurulumla, bazı şeyler istikrarı geliştirir / değiştirir, ancak yine de mükemmel bir yere yakın değildir:

Histereziyi arttırmak için R5'in değerinin değiştirilmesi. C2'yi tamamen kaldırmak (neden olduğu hakkında hiçbir fikrim yok). Breadboard'daki tellere dokunmak (oldukça az sayıda yan yana). Güç kaynaklarını hariciten USB'ye veya tersi yönde değiştirme.

Bu noktada, ya gürültü, sinüs dalgası ürettiğim DAC'ım ya da çok temel bir şeyi yanlış yapıyorum. Bu devre diğer insanlar için sorunsuz çalıştı, bu yüzden yapılandırmam veya ortamımla ilgili bir sorun olması gerekiyor.

Herhangi bir öneriniz varsa, zaman ayırdığınız için çok teşekkür ederim.

İşte asgari kaynağım:

#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);
    }
}

Ayrıca, devre şemasına ve kütüphanenin kendisine olan bağlantı:

http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/

GÜNCELLEME:

Tüm önerilerinizi denedim, hiçbiri işe yaramadı, biri değil. Kesme bayraklarını temizlemek veya ISR'nin içindeki veya dışındaki kesintileri devre dışı bırakmak gerçekten bir etkiye sahip değildi. Çipin karşılaştırıcı kaydının gerçekten nasıl çalıştığını yanlış anlıyorum.

Başlangıçta bahsettiğim gibi, sinüs dalgasından elde edilen kare dalganın frekansını ölçmek için girdi yakalamayı kullanacaktım. Karşılaştırıcının çıkışı giriş yakalama pimine beslenir, daha sonra süreyi ölçmek için zamanlayıcıları kullanın, basit.

İşte atmega2560'ın analog karşılaştırıcı şeması http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , sayfa 265:

resim açıklamasını buraya girin

Gördüğünüz gibi, karşılaştırıcının ACO ve ACIS0 + ACIS1 olmak üzere iki çıkışı vardır. ACO, + input> - input olduğunda ayarlanır, + input <- input olduğunda silinir. ACIS0 + ACIS1 kenar seçme bitleridir.

Başlangıçta yaptığım şey ISR'mdeki kenar tipini kontrol etmekti. Bunun yerine ISR'yi değiştirdim:

    ISR(ANALOG_COMP_vect) {

     if (!(ACSR &  (1<<ACO))) { // + < -
         /* Set PIND5 to 0V */
         PORTD &= ~(1 << PIND5);
    }
    else  {
       /* Set PIND5 to 5V */
       PORTD |= (1 << PIND5);
    }
}

Ve çıktı kusursuz davrandı (tıpkı ikinci resimde olduğu gibi). Sonra bakliyatların genişliğini ölçmeye devam ettim ama sonuçlar iyi değildi. LCD ekranımda yoğun geçiş, sayılar rastgele bir değere atlıyor veya temiz bir sinyale sahip olmasına rağmen 0'da kalıyor. Kodumu birçok kez farklı koşullar kullanarak yeniden yazdım, şimdiye kadar aldığım tek yarı kararlı çözüm şudur:

#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);
}

Yarı kararlı demek istediğim, doğru değeri 1/3 oranında alıyorum. Diğer zamanların 2 / 3'ü doğru değerin yarısı veya rastgele bir değerdir. Koşullu ifadeler için zamanlayıcı kayıt bitlerini ve karşılaştırıcısının kayıt bitlerini ISR'mde kullanmayı denedim, bu tür işler sadece yapılandırma.

Gün içinde daha sonra yaptığım aynı kurulum ve kaynak yerine harici bir karşılaştırıcı kullanmak oldu (karşılaştırıcı ile ilgili tüm satırlar hariç). Çıkışı giriş yakalama pimine beslendi ve istendiği gibi çalıştı (histerezise bile gerek yoktu).

Bu noktada, harici bir karşılaştırıcı kullanarak çözdüğümü söyleyebilirim, ancak dahili olanın neden davranmadığı hakkında hiçbir fikrim yok. Bu konuda birçok yazı ve rehber okudum, farklı kütüphaneleri okudum, kabul edilebilir bir sonuç olmadan taklit etmeye çalıştım. Veri sayfasında tüm karşılaştırıcı biriminde sadece 5 sayfa var, birçok kez tekrar okuyorum ve ne yaptığımı göremiyorum.

Nasıl düzgün kullanılacağını öğrenmek istiyorum ama bu başarısız olursa bir yedek var. Başka bir girişiniz varsa, büyük beğeni topluyor.


4
yeni başlayanlar için ... çıkış ve + ve girişi arasına 1M direnç ekleyin. Bu, sadece referansı değiştiren
R5'inizi

1
Çipin içindeki ve erişilemeyen bir karşılaştırıcıdan çıkışın kapsam resimlerini nasıl üretebilirsiniz?
Andy aka

2
Bir ISR'ye girdiğinizde daha fazla kesintiyi mi devre dışı bırakıyorsunuz? İhtiyacınız olabilir - çoğu ISR'nin çift isabet alması olabilir.
Andy aka

1
Histerezis pimini nasıl değiştiriyorsunuz ve mevcut değere göre niteliyor musunuz? Kesme ve değiştirme arasındaki gecikme size zarar veriyor olabilir.
Trevor_G

1
şemanızda gösterilmeyen pin5 ve pin6 arasındaki dahili kapasitans, bunun yerine histerizis yapmak için pin7'deki dahili pull-up'ı kullanabilir misiniz?
Jasen

Yanıtlar:


13

Sinüs dalgası sinyalini üretmek için bir DAC kullandığınızı okudum. DAC çıkışları çıkış durumu değişikliklerinde aksabilir, bu nedenle karşılaştırıcı devrenize beslemeden önce DAC çıkışına kesinlikle bazı analog filtreler uygulamanız gerekir. Bu, meydana gelmesi muhtemel çift kesme tetikleyicilerinin bazılarının önlenmesine yardımcı olabilir.

Ayrıca, bu tür bir sorun için harici bir karşılaştırıcı kullanmak istediğinizi de söyleyebilirim, böylece histereziyi bir yazılım etkileşimi kullanmadan dirençlerle uygulayabilirsiniz. Bu, karşılaştırıcının çıktısını doğrudan izleyebileceğiniz için daha iyi sorun izolasyonuna da izin verecektir.

Son yorum, kullandığınız histerezis türü ile ilgilidir. Tam olarak hangi şemayı kullandığınızı görmek biraz zordur, ancak istediğiniz şeyin bunu yapan davranış olduğunu unutmayın: Eşik voltajını sinyal geçişinden OPPOSİT yönünde çeken histerezis istersiniz. Böylece yükselen bir kenar için eşiğin sıfır noktasından biraz daha yüksek olmasını istersiniz ve sonra durum değiştiğinde eşik daha düşük bir seviyeye çekilir.


1
Histerezis yönünün nasıl çalışması gerektiğine dair ekstra açıklama için +1. Paragraf 2 iyi bir tavsiyedir, ancak bu örnekte durum böyle görünmüyorsa, doğru yapılması şartıyla dahili olarak yapılması da uygundur.
Trevor_G

@Trevor_G -: ^)
Michael Karas

1
@Hypomania - ISR'deki tek zamanlayıcıyı okuyabileceğinizi biliyorum. Ancak zamanlayıcı çift tamponlu değilse, bir çıkış yazmacı sayımı tetikleyiciden tutarken zamanlayıcının kendisi saymaya devam edebilirse, zamanlayıcıyı durdurabilir, böylece okuyabilir ve okuduktan sonra tekrar etkinleştirebilirsiniz. . Birçok MCU zamanlayıcısı bu şekilde iki kez arabelleğe alınmaz ve böylece zamanlayıcının yeniden etkinleştirildiği zamana kadar ISR'ye girme işlem süresi, sonraki yarım döngü için dönem süresi ölçümünde zaman kaybı olur. Zamanlayıcının ne kadar hızlı saat hızlandığına (devam) bir dereceye bağlıdır
Michael Karas

1
(yukarıdan devam eder) ancak sayıyı değiştirmek için bir saatin aynı anda gelebileceği bir sayı değeri okuduğunuzda asla olmak istemezsiniz. Zamanlayıcının bir tetikleyici yakalama olayında iki kez arabelleğe alınıp alınmadığını görmek için kullandığınız belirli MCU'yu araştırmadım.
Michael Karas

1
@Hypomania - Bir hevesle bağlantılı AVR MCU veri sayfasına baktım ve zamanlayıcı giriş yakalama fonksiyonunun çift tamponlu olduğunu görüyorum !! Nitekim bu parçalardaki zamanlayıcı oldukça sağlam görünüyor. Herhangi bir AVR parçası kullandığımdan bu yana yaklaşık 15 yıl geçti.
Michael Karas

6

Bu senaryo ile ilgili sorunlar, karşılaştırıcı anahtarlama ve kesme "histerezis" pimi değiştirdiğiniz noktaya işlenmesi arasında bir gecikme olmasıdır.

Histeresis bandınız, ne için kullandığınızı dikkate alarak bu sinyal seviyesi için oldukça küçüktür. Özellikle kapsamınızdaki kare dalgada ne kadar gürültü olduğunu gördüğümde.

Bu faktörlerin her ikisi de göz önünde bulundurulduğunda, belirli giriş seviyelerinde ilkini işleyebilmeniz için karşılaştırıcıdan birden fazla kenar alma olasılığınız yüksektir. Bu kesme işleyici sırasında karşılaştırıcı durumunun ne olduğunu görmek için kontrol, her iki durumda da olabileceğinden çok yardımcı olmaz.

Ne yazık ki işleyicinin nasıl çalıştığı sorusunda ayrıntılı bilgi vermediniz.

Ancak işleyiciniz böyle bir şey yapmalıdır.

  1. Yüksek eşik durumunda histerezis değeri negatif kenar kesmesi için beklemeniz gerekir.

  2. Bahsedilen negatif kenar kesmesi geldiğinde, histerezi düşük değere getirin, birkaç döngü bekleyin, ardından bekleyen kesintileri temizleyin ve pozitif bir kenar kesmesi için beklemeye başlayın.

  3. Bahsedilen pozitif kenar kesmesi geldiğinde, histerezis pimini tekrar yüksek değere getirin, birkaç döngü bekleyin, beklemedeki kesintileri temizleyin ve negatif kenar kesmesini tekrar beklemeye başlayın.

  4. 1. adımdan itibaren tekrarlayın.

BTW Sinyal referansı olarak karşılaştırıcı referansını kullanma şeklinize çok meraklı değilim. Bu, hem sinyalden referansa hem de histerezistten sinyale, özellikle düşük frekanslı sinyallerle küçük bir çapraz konuşma ile sonuçlanır. Bu değerlerle verilen etki küçük olmalıdır, ancak saflık için, sinyal üzerindeki ayrı bir önyargı daha iyi olacaktır.

EDIT: Kodunuzu yeniden.

Else deyiminde, histereziyi ayarlamadan önce kesme kenarını değiştirirsiniz.

Her iki durumda da geri dönmeden önce bekleyen kesintileri duraklatmaz ve temizlemezsiniz. (Kesme kontrol kaydının değiştirilmesi kendi başına kesme oluşturabilir.)

Atmega'nın yeniden kesip kesmediğini bilmiyorum, yani sonraki bir kenar hala çalışan işleyiciyi önceki kenardan kesecekse. Eğer öyleyse, eşzamanlılığı uygun şekilde ele almanız gerekir.

PORTC parçasının ne için olduğundan emin değilim, ancak muhtemelen nitelikli parçaya taşınması gerekiyor.


Çok teşekkür ederim, önerilerinizi yarın deneyeceğim ve size bir güncelleme vereceğim. ISR'ye gelince, beklemeyi hariç tuttuğunuz senaryo için if-else if deyim var.
Shibalicious

1
@Hypomania sorunuzu düzenlemeli ve kesme işleyicinin kodunu göndermelisiniz ki böylece insanlar bir yere karışıp karışmadığınızı görebilecekler. Ancak gürültü olabilir, kapsam izleriniz orada 50mV'den fazla gürültü varmış gibi görünüyor. Hala gürültü ile kendini düzeltmesini beklerdim, hepsi geçişlerde ara sıra ekstra darbelerle olsun.
Trevor_G

Ben de öyle bekliyordum. En kısa zamanda yapacak.
Shibalicious

1
@Hypomania see edit
Trevor_G

1
@ Hipomani Çünkü bu iki komut arasında başka bir kesinti elde edebilirsiniz. Ayrıca düzenlemeyi de düzenledim ...
Trevor_G

3

Bu efekt, temas sıçramasına benzer ve butonlar için kullandığınız aynı kaldırma teknikleri ile azaltılabilir.

  • Kalkış saatine karar verin Td
  • Son kenar kesmesinin zaman damgasını bir değişkente tut
  • akım kesintisi ile son kesinti arasındaki süre daha azsa Td, akım kesintisini yok sayın
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.